还原virtual函数的本质-----C++

当你每次看到C++类中声明一个virtual函数,特别是看到了一个virtual的虚构函数。你知道它的意思吗?你肯定会毫不犹豫的回答:不就是多态么。。。在运行时确定具体的行为么。。。完全正确,但这里我要讲的不只是这些东西。

 有些类需要虚函数,有些不需要虚函数。这是为什么,一般你看到的类如果有一个虚析构函数,那么这个类中应该会有至少一个是虚函数的。。这是为什么呢??如果我们类中没有用其他虚函数的话,你创建了这个也是多余的,而且会增加类对象的大小。。说这些纯理论的东西,也许大家不知所云。。下面我就给例子来验证。。

1:

 

class A
{
public:A(){};
//	virtual ~A(){};~A();
};
void main()
{A a;cout<<sizeof(a)<<endl;
}


结果为1。这个1应该是编译器自己为它加上的。。哪怕你不在类中不写任何东西,它也是1;例如;

 

 

 

class A
{};
void main()
{A a;cout<<sizeof(a)<<endl;
}


如果你把析构函数声明为虚函数。。如:

 

 

2:

class A
{
public:
A(){};
virtual ~A(){};
//~A();
};
void main()
{


A a;
cout<<sizeof(a)<<endl;
}

结果是4。先不说这是为什么。。


然后还是说一下关于虚函数基础的东西(多态)吧,也给个例子:

 

#include<iostream>
#include<string>
using namespace std;class Base
{
public:Base();virtual ~Base();virtual  void test();
private :int count;
};
Base::Base(){cout<<"Base部分创建了"<<endl;
}
Base::~Base(){cout<<"Base部分被销毁了"<<endl;
}
void Base::test()
{cout<<"Base Test"<<endl;}class Derive1:public Base
{
public:Derive1();virtual  ~Derive1();void test();};
Derive1::Derive1(){cout<<"子类部分创建了"<<endl;
}
Derive1::~Derive1(){cout<<"子类部分被销毁了"<<endl;
}
void Derive1::test()
{cout<<"Derive1 Test"<<endl;
}void main()
{Base* d1=new Derive1();d1->test();delete d1;}


 

 


由此看见,当通过声明一个父类指针并且让它指向一个子类的对象,在子对象创建的时候,会先去调用父类的构造函数,然后再是自己的构造函数,当通过父类指针去调用一个虚函数test()时,它实际上回去调用子类的test()函数,这是为什么呢,它肯定有什么信息让它这样做吗。。这个信息肯定是子类对象给它的。。这个信息就是虚函数指针(vptr),它指向一个虚函数表(vtbl),这个虚函数表其实就是包含了这个类的所有虚函数的函数名(函数指针),每个类就只包含了那一个虚函数指针和它的一些成员变量。这下可以解释上面为什么是1,为什么是4了。。

 在win32的机器上,每个指针是4字节。刚才也提到每个类的大小取决于两部分,一个是成员变量,一个是虚函数指针而且有且只有一个,在例子一中,因为没有成员变量,而有一个虚函数---析构函数,此时肯定会有一个虚函数指针,所以是4。。 其实刚才也就同时说清楚了多态的本质,就是子对象的虚函数指针给出了这个信息,父类指针才知道去执行哪个函数。。

最后一个问题:为什么析构函数要声明为虚函数呢?(当至少有一个为虚函数的时候)

从刚才的那个结果也可以看出,当我们delete那个指针的时候,会发生析构,而且这个过程是从子类到父类的顺序进行。假如此时析构函数不为虚函数,父类指针也就不知道去执行子类的析构函数。。也就不会去释放子对象的那部分内存,造成内存泄漏。。例如:(这里我们只是对上一段代码进行修改,去掉了父类中的virtual):

 

#include<iostream>
#include<string>
using namespace std;class Base
{
public:Base();~Base();virtual  void test();
private :int count;
};
Base::Base(){cout<<"Base部分创建了"<<endl;
}
Base::~Base(){cout<<"Base部分被销毁了"<<endl;
}
void Base::test()
{cout<<"Base Test"<<endl;}class Derive1:public Base
{
public:Derive1();~Derive1();void test();};
Derive1::Derive1(){cout<<"子类部分创建了"<<endl;
}
Derive1::~Derive1(){cout<<"子类部分被销毁了"<<endl;
}
void Derive1::test()
{cout<<"Derive1 Test"<<endl;
}void main()
{Base* d1=new Derive1();d1->test();delete d1;}

 

 

所以当至少有一个虚函数的话,我们也要把它的析构函数声明为virtual。(插一句:有些人会说你子类中的那个函数哪里是虚函数哦,我没看到virtual 啊。。其实C++允许我们这样做,重写父类的虚函数,不是必须要声明出来的。)

总结:一个类有了虚函数,是为了成为一个基类,如果不是这样的话,那么父类中的任何函数都没有必要是虚函数,甚至会增加类的大小。多态告诉了我们这点。。一旦成为了基类,那么就要把析构函数声明为一个虚函数。。

好了,虚函数的内容就Over了。。。。。

 

转载于:https://www.cnblogs.com/pangblog/p/3258031.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/261342.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

0511 backlog 项目管理

SCRUM 这次的作业就是确定SCRUM的计划&#xff0c;确定sprint backlog的一个冲刺周期&#xff0c;而这个周期是两个星期。争取在两周内发布1.0版本。 本次作业以网站构建为主&#xff1a; ID NAMEIMPESTHOW TO DONOTES1首页99小时用户登录网站也可以看游客的推广的内容&am…

4月27日微软云训练营活动-现场图集

1.签到 2.到场同学&#xff0c;这一天是工作日&#xff0c;但是人气依然很火。 转载于:https://www.cnblogs.com/finehappy/p/3262296.html

Java中内存中的Heap、Stack与程序运行的关系

堆和栈的内存管理 栈的内存管理是顺序分配的&#xff0c;而且定长&#xff0c;不存在内存回收问题&#xff1b;而堆 则是随机分配内存&#xff0c;不定长度&#xff0c;存在内存分配和回收的问题&#xff1b;堆内存和栈内存的区别可以用如下的比喻来看出&#xff1a;使用堆内存…

mysql 5.6 linux安装配置_linux手动安装配置mysql5.6

1.准备工作①官网下载&#xff1a;https://dev.mysql.com/downloads/mysql/5.6.html#downloads下载之后上传到服务器。②创建linux组用户groupadd mysqluseradd -g mysql mysql2.安装①解压&#xff0c;比如放到了/usr/local/,进入到该目录下&#xff0c;进行用z解压gz包&#…

KVM安装Windows Server 2008 R2使用virtio硬盘

在上一篇文章中&#xff0c;我们介绍了使用IDE硬盘来安装Windows Server 2008 R2,这篇文章我们来介绍使用virtio硬盘来安装Windows Server 2008 R2。 说明&#xff1a;KVM默认使用的硬盘格式为virtio。 使用virtio接口的硬盘&#xff0c;我们必须加载virtio硬盘驱动。如果不加载…

Sublime Text 2 入门及技巧

看了 Nettuts 对 Sublime Text 2 的介绍&#xff0c;立刻就兴奋了&#xff0c;诚如作者 Jeffrey Way 所说&#xff1a;“《永远的毁灭公爵》都发布了&#xff0c;TextMate 2 还没发”&#xff0c;你还能指望它么&#xff1f;TextMate 开发者的消极态度已经无法让人忍受了。而作…

YII 配置文件

用YIIFramework的库开发 Java代码 .... Yii::createWebApplication($config); //没有run Yii::import&#xff08;class1&#xff0c;true&#xff09;,在将class1类文件路径存储时&#xff0c;同时include该文件 注意&#xff1a;你也可以将配置文件分为多个文件&#xff0…

mysql装完后navicat无法连接_重装mysql后导致Navicat连接失败

今天重装了mysql数据库&#xff0c;然后再使用navicat去连接数据库的时候&#xff0c;一直报错 1251 Client does not support authentication protocol requested by server解决方法&#xff1a;1、cmd登录mysql2、修改Navicat中连接数据库的密码3、刷新mysql的系统权限表flus…

怎样创建XML文档

在程序中&#xff0c;我们怎样创建一个XML文档。下面演示中&#xff0c;Insus.NET在程序创建一个和http://www.cnblogs.com/insus/p/3274220.html 一模一样的XML文档。可以在HTML markup放一个铵钮&#xff1a; 去.aspx.cs写按钮事件&#xff1a; 上图代码示例中&#xff0c;右…

Windows Server 2008设置远程桌面连接的最大数量

远程桌面连接的默认数量是2&#xff0c;当有多个用户需要同时远程桌面连接时很不方便&#xff0c;可以设置远程桌面连接的最大数量。 1. 运行gpedit.msc&#xff1b; 2. 选择计算机配置-->管理模板-->Windows组件-->远程桌面服务-->远程桌面会话主机-->连接&…

错误: 元素值必须为常量表达式_C语言编程常见错误集锦 【下】

1、输入数据时&#xff0c;规定精度输入数据时不能规定精度。2、switch语句中漏写break语句由于漏写了break语句&#xff0c;case只起标识的作用&#xff0c;而不起判断的作用。当grade的值为A时&#xff0c;程序会从上到下执行完&#xff0c;五个printf都输出。正确的写法应为…

Hadoop概述

为什么80%的码农都做不了架构师&#xff1f;>>> 一&#xff1a;Hadoop的相关概念 1、Hadoop是一个基于java语言的MapReduce框架。 2、Hadoop的改进&#xff1a; a、Hadoop Streaming--任何命令行脚本都可以通过Streaming调用MapReduce框架。 b、Hadoop Hive&#xf…

WHU 1470 Join in tasks 水题

http://acm.whu.edu.cn/land/problem/detail?problem_id1470 大概是给你一个队列,每次移动队头的数到队尾并减1,如果本身这个数为1就删去. 然后ans 这个数 * (队列长度-1),求最小的ans 只要最小的元素最先删除就能保证结果最小 解法: 先对原数列排序 然后模拟原操作 ...但是…

TF-IDF理解及其Java实现

TF-IDF 前言 前段时间&#xff0c;又具体看了自己以前整理的TF-IDF&#xff0c;这里把它发布在博客上&#xff0c;知识就是需要不断的重复的&#xff0c;否则就感觉生疏了。 TF-IDF理解 TF-IDF&#xff08;term frequency–inverse document frequency&#xff09;是一种用于资…

PostgreSQL 9.2迁移到9.3

Netkiller PostgreSQL 手札 Mr. Neo Chan, 陈景峰(BG7NYT) 中国广东省深圳市龙华新区民治街道溪山美地51813186 1311366889086 755 29812080<netkillermsn.com> 文档始创于2012-11-16 版权 © 2010, 2011, 2012, 2013 Netkiller(Neo Chan). All rights reserved. 版…

C#原型模式之深复制实现

SYSTEM空间有ICONEALBE接口。。。因为其太常用。 1 /*2 * Created by SharpDevelop.3 * User: home4 * Date: 2013/4/215 * Time: 22:206 * 7 * To change this template use Tools | Options | Coding | Edit Standard Headers.8 */9 using System;10 11 namespace Res…

python flask框架教程_Flask框架从入门到实战

Flask简介&#xff1a;Flask是一个使用 Python 编写的轻量级 Web 应用框架&#xff0c;基于 WerkzeugWSGI工具箱和 Jinja2模板引擎。使用 BSD 授权。Flask也被称为 “microframework” &#xff0c;因为它使用简单的核心&#xff0c;用 extension 增加其他功能。Flask没有默认使…

iphone开发中数据持久化之——属性列表序列化(一)

数据持久化是应用程序开发过程中的一个基本问题&#xff0c;对应用程序中的数据进行持久化存储&#xff0c;有多重不同的形式。本系列文章将介绍在iphone开发过程中数据持久化的三种主要形式&#xff0c;分别是属性列表序列号、对象归档化以及iphone的嵌入式关系数据库SQLite。…

对话jQuery之父John Resig:JavaScript的开发之路

在参加完CSDN组织的TUP对话大师系列演讲活动后&#xff0c;27岁的jQuery之父John Resig接受了本刊总编刘江的深度访谈&#xff0c;这篇对话文章&#xff0c;让我们一窥这位著名程序员的人生及技术感悟。 编程初体验 《程序员》&#xff1a;你是如何开始编程的&#xff1f; John…

互联网产品研发的典型流程

这张图是互联网产品研发的一种最佳实践&#xff0c;这张图中没有包含异常流的处理。通常异常出现在进入开发甚至测试阶段了还在变更需求&#xff0c;进入封版发版阶段了还在修改代码&#xff0c;所以在这两个时间点都有需求冻结和代码冻结。 转载于:https://www.cnblogs.com/mo…