C++虚函数表与虚析构函数
1.静态联编和动态联编
联编:将源代码中的函数调用解释为要执行函数代码。
静态联编:编译时能确定唯一函数。
在C中,每个函数名都能确定唯一的函数代码。
在C++中,因为有函数重载,编译器须根据函数名,参数才能确定唯一的函数代码。
动态联编:编译时不能确定调用的函数代码,运行时才能。
C++中因为多态的存在,有时编译器不知道用户将选择哪种类型的对象,因此无法确定调用的唯一性,只有在运行时才能确定。
2.虚成员函数,指针或引用,动态联编
指针或引用才能展现类的多态性。
当类中的方法声明为virtual时,使用指针或引用调用该方法,就是动态联编。
若是普通方法,则为静态联编。
示例如下:
class Test
{
public:
virtual show()
{
std::cout<<"Test::show()"<<std::endl;
}
};
class SubTest:public Test
{
public:
virtual show()
{
std::cout<<"SubTest::show()"<<std::endl;
}
};
int main()
{
SubTest subTest;
Test * p = &subTest;//指向子类的指针
Test & a = subTest;//子类的引用
Test * p2 = new Test;//指向父类的指针
p->show();
a.show();
p2->show();
return ;
}
程序没有释放内存,我们将在后面析构函数的时候,完善该程序。
3.动态联编使用原则
动态联编,需要跟踪基类指针或引用指向的实际对象类型,因此效率低于静态联编。
1)当类不会用作基类时,成员函数不要声明为virtual
2)当成员函数不重新定义基类的方法,成员函数不要声明为virtual
4.关于虚函数
1)父类成员函数若声明为virtual,则子类中也是虚的,若要重新定义该方法,可显式加上virtual关键字,也可不加,编译器编译时会自动加上。
2)使用指向对象的引用或指针来调用虚方法,将使用具体对象类型定义的方法,而不一定是引用或指针类型定义的方法。
SubTest subTest;
Test * p = &subTest;//指向子类的指针
p->show();//将调用SubTest对象定义的show()方法
5.虚函数的工作原理
当类中存在虚函数时,编译器默认会给对象添加一个隐藏成员。该成员为一个指向虚函数表(virtual function table,vtbl)的指针。
虚函数表是一个保存了虚函数地址的数组。编译器会检查类中所有的虚函数,依次将每个虚函数的地址,存入虚函数表。
class Test
{
public:
virtual show()
{
std::cout<<"Test::show()"<<std::endl;
}
private:
int a;
};
class SubTest:public Test
{
public:
virtual show()
{
std::cout<<"SubTest::show()"<<std::endl;
}
};
内存结构图如下所示:

可以看出,父类和子类有独立的虚函数表,且虚函数表中虚函数指针也指向各自的虚函数地址,
若子类没有覆盖父类中的show方法,则虚函数指针show_ptr指向的虚函数show()的地址是一样的,均指向父类show()函数地址。虚函数表的存在和动态联编,就是多态的原理。
6.虚析构函数
1)构造函数是特殊的,是没有虚函数的概念的。
构造函数是不继承的,创建子类对象时,将调用子类的构造函数,子类的构造函数将自动调用父类的构造函数。
2)析构函数应是虚函数,除非类不用做基类。
我们看下面的代码:
Test *p = new SubTest;
delete p;
p=NULL;
由虚函数表,我们知道,若析构函数不声明为virtual,则调用的将是Test类的析构函数,而没有调用SubTest类的析构函数,此时造成了内存泄露。
所以析构函数必须声明为虚函数,调用的将是子类SubTest的析构函数,
我们还需要知道的一点是,子类析构函数,一定会调用父类析构函数,释放父类对象,则内存安全释放。
我们第一个例子的完整的示例代码如下:
class Test
{
public:
virtual show()
{
std::cout<<"Test::show()"<<std::endl;
}
virtual ~Test(){}
};
class SubTest:public Test
{
public:
virtual show()
{
std::cout<<"SubTest::show()"<<std::endl;
}
};
int main()
{
SubTest subTest;
Test * p = &subTest;//指向子类的指针
Test & a = subTest;//子类的引用
Test * p2 = new Test;//指向父类的指针
p->show();
a.show();
p2->show();
delete p;
p=NULL;
delete p2;
p2=NULL;
return ;
}
参考资料:《C++ Primer.Plus》 pp.490-507
http://www.imooc.com/video/9199
C++虚函数表与虚析构函数的更多相关文章
- C++对象的内存布局以及虚函数表和虚基表
C++对象的内存布局以及虚函数表和虚基表 本文为整理文章, 参考: http://blog.csdn.net/haoel/article/details/3081328 http://blog.csd ...
- C++ 中的虚函数表及虚函数执行原理
为了实现虚函数,C++ 使用了虚函数表来达到延迟绑定的目的.虚函数表在动态/延迟绑定行为中用于查询调用的函数. 尽管要描述清楚虚函数表的机制会多费点口舌,但其实其本身还是比较简单的. 首先,每个包含虚 ...
- 从零开始学C++之虚函数与多态(一):虚函数表指针、虚析构函数、object slicing与虚函数
一.多态 多态性是面向对象程序设计的重要特征之一. 多态性是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为. 多态的实现: 函数重载 运算符重载 模板 虚函数 (1).静态绑定与动态绑 ...
- C++对象内存分布详解(包括字节对齐和虚函数表)
转自:https://www.jb51.net/article/101122.htm 1.C++对象的内存分布和虚函数表: C++对象的内存分布和虚函数表注意,对象中保存的是虚函数表指针,而不是虚函数 ...
- C++虚函数与虚函数表
多态性可分为两类:静态多态和动态多态.函数重载和运算符重载实现的多态属于静态多态,动态多态性是通过虚函数实现的. 每个含有虚函数的类有一张虚函数表(vtbl),表中每一项是一个虚函数的地址, 也就是说 ...
- 详谈C++虚函数表那回事(一般继承关系)
沿途总是会出现关于C++虚函数表的问题,今天做一总结: 1.什么是虚函数表: 虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的.简称为V-Table. ...
- 【C++ Primer | 15】C++虚函数表剖析②
多重继承 下面,再让我们来看看多重继承中的情况,假设有下面这样一个类的继承关系. 注意:子类只overwrite了父类的f()函数,而还有一个是自己的函数(我们这样做的目的是为了用g1()作为一个标记 ...
- C++ 关于类与对象在虚函数表上唯一性问题 浅析
[摘要] 非常多教材上都有介绍到虚指针.虚函数与虚函数表.有的说类对象共享一个虚函数表,有的说,一个类对象拥有一个虚函数表.还有的说,不管用户声明了多少个类对象,可是,这个VTABLE虚函数表仅仅有一 ...
- 深入分析C++虚函数表
C++中的虚函数(Virtual Function)是用来实现动态多态性的,指的是当基类指针指向其派生类实例时,可以用基类指针调用派生类中的成员函数.如果基类指针指向不同的派生类,则它调用同一个函数就 ...
随机推荐
- win7 mtp
Sensors MTP Monitor Service 允许MTP设备数据传输,如果该服务被禁用,MTP监视器将不能工作.该服务的默认运行方式是手动,如果你没有使用MTP设备,该功能就可以放心禁用. ...
- linux安装JDK环境,JDK6.0即java 1.6.0
下载 (1)更改权限:# chmod 755 jdk-1.6.0_23-linux-i586.rpm.bin 执行安装:# ./ jdk-1.6.0_23-linux-i586.rpm.bin 此步完 ...
- openerp 7.0接收陌生邮件名称显示乱码问题解决方法
修改代码:addons\mail\mail_thread.py #858 line #msg_dict['email_from'] = decode(message.get('from')) ms ...
- sqlserver profiler 抓出来作业的代码 SQLAgent - TSQL JobStep,二进制作业名字转化为字段串作业名字,job_id
sqlserver 中 profiler 抓出来不少是作业中的代码,applicationname 类似于 SQLAgent - TSQL JobStep (Job 0x94B9B5C016E8D94 ...
- STL之lambda表达式
一.简介 我们可以向一个算法传递任何类别的可调用对象.对于一个对象或一个表达式,如果可以对其使用调用运算符,则称它为可调用的.即,如果e是一个可调用的表达式,则我们可以编写代码e(args),其中ar ...
- POJ--2752--Seek the Name, Seek the Fame【KMP】
链接:http://poj.org/problem? id=2752 题意:对于一个字符串S,可能存在前n个字符等于后n个字符,从小到大输出这些n值. 思路:这道题加深了对next数组的理解.next ...
- openssl的证书格式转换
证书转换 PKCS 全称是 Public-Key Cryptography Standards ,是由 RSA 实验室与其它安全系统开发商为促进公钥密码的发展而制订的一系列标准,PKCS 目前共发布过 ...
- esp8266烧录Html文件,实现内置网页控制设备!
代码地址如下:http://www.demodashi.com/demo/14321.html 一.前言: 这个月也快结束了,时间真快,我服务器知识自学依然在路途中,这几天听到热点网页配置esp826 ...
- js实现new Date(),时间对象和时间戳操作
1.js中实现时间date对象 var myDate = new Date();//获取系统当前时间,结果:Wed Aug 09 2017 00:00:00 GMT+0800 (中国标准时间) 2.获 ...
- C-从源文件到可执行文件的详细编译链接过程
一直用windows一键搞定, 没有去了解详细的编译链接过程, 今天看了一篇文章, 顺便实验和记录在Linux下逐步生成的步骤. 预处理: 执行#include, #define, #if, #ifd ...