C++中虚函数详解二
来源:互联网 发布:降低音量软件旧版 编辑:程序博客网 时间:2024/05/29 03:35
文章出处:
http://blog.csdn.net/shift_wwx/article/details/78622643
前言:
《C++中虚函数详解一》详细解释了在单一继承中虚函数的使用,这一文来详细解释下在多重继承中虚函数的使用。
1、多重继承重写父类的虚函数
先来看实例代码:
class Base1 {public:Base1() : value(1) { cout << "Base1 constructor..." << endl; }~Base1() { cout << "Base1 destructor..." << endl; }virtual void f1() { cout << "base1 function f1..." << endl; }virtual void f2() { cout << "base1 function f2..." << endl; }virtual void f3() { cout << "base1 function f3..." << endl; } int value;};class Base2 {public: Base2() : value(2) { cout << "Base2 constructor..." << endl; } ~Base2() { cout << "Base2 destructor..." << endl; } virtual void f1() { cout << "Base2 function f1..." << endl; } virtual void f2() { cout << "Base2 function f2..." << endl; } virtual void f3() { cout << "Base2 function f3..." << endl; } int value;};class FromBase : public Base1, public Base2 {public:FromBase() : value(3) { cout << "FromBase constructor..." << endl; }~FromBase() { cout << "FromBase destructor..." << endl; }virtual void f2() { cout << "FromBase function f2..." << endl; } int value;};定义了两个base的类作为父类,定义FromBase类作为子类。其中f2()是重写的函数。
测试代码如下:
void Test2::testBase1(long **ptl) { Base1 *p; p = (Base1*)ptl; cout << "address base1 is: " << p << endl; cout << "value vptr is: = 0x" << uppercase << hex << *ptl << endl; cout << "address base1 value is:" << &(p->value) << endl; long *vptr = *ptl; Fun fun = (Fun)*(vptr + 0); fun(); fun = (Fun)*(vptr + 1); fun(); fun = (Fun)*(vptr + 2); fun();}void Test2::testBase2(long **ptl) { Base2 *p; p = (Base2*)ptl; cout << "address base2 is: " << p << endl; cout << "value vptr is: = 0x" << uppercase << hex << *ptl << endl; cout << "address base2 value is:" << &(p->value) << endl; long *vptr = (long*)*ptl; Fun fun = (Fun)*(vptr + 0); fun(); fun = (Fun)*(vptr + 1); fun(); fun = (Fun)*(vptr + 2); fun();}void Test2::testFromBase(long **ptl) { cout << "address FromBase is: " << ptl << endl; cout << "value vptr is: = 0x" << uppercase << hex << *ptl << endl; long *vptr = (long*)*ptl; Fun fun = (Fun)*(vptr + 0); fun(); fun = (Fun)*(vptr + 1); fun(); fun = (Fun)*(vptr + 2); fun();}void Test2::test1() { long **ptl = NULL;Base1 base1; Base2 base2; FromBase fbase; ptl = (long**)(&base1); testBase1(ptl); cout << endl << endl; ptl = (long**)(&base2); testBase2(ptl); cout << endl << endl; ptl = (long**)(&fbase); testFromBase(ptl); cout << "test1 end..." << endl;}运行结果如下:
对于Base1和Base2对象的内存分布,这里不做重复的解释,详细看《C++中虚函数详解一》,主要看下两个类对象的虚函数表情况:
对象base1的虚函数表内存中分布:
所以虚函数表结构表示为:
对象base2的虚函数表内存分布:
所以虚函数表的结构表示为:
最后来看一下fbse对象的内存分布:
从这里看到有两张虚函数表,每个虚函数头指针后面还有value的值。网上很多文章可能会误导人,都以为虚函数表都是挨着的,其实不是的。
暂时跳过对象的内存分配,下面会总结。先来看两张虚函数表:
对比base1和base2的虚函数表,可以发现只要是子类中有可以重写父类的函数,在虚函数表中都会进行覆盖。
这也就是解释了为什么子类对象通过父类的指针能找到子类的函数。例如,
Base1 *p1 = &fbase;//其实指的是0x2AFC58Base2 *p2 = &fbase;//其实指的是0x2AFC60
这样通过上面的指针找到虚函数的头指针,然后找到对应的虚函数表,这样能找到子类的重写的函数了。
这就是多态性的原理,通过基类指针能找到不同对象的所要处理的函数,实现了同样的消息实现不一样的结果。
另外,对于多重继承第一张虚函数表最后并没有出现之前单一继承中出现的0,而多重继承最后一张虚函数表最后出现的是0。这个有个疑问,谁知道为什么吗?跟编译器有关系吗?
2、多重继承没有父类的虚函数
把子类FromBase中的虚函数改一下:
class FromBase : public Base1, public Base2 {public:FromBase() : value(3) { cout << "FromBase constructor..." << endl; }~FromBase() { cout << "FromBase destructor..." << endl; }virtual void fb() { cout << "FromBase function fb..." << endl; } int value;};运行结果这里不再重复,主要来看对象fbase的内存分配:
显然还是两张表,来看下两张虚函数表:
最后发现子类自身的虚函数会放在第一张虚函数表中。
总结:
- 不同的父类中的虚函数存放在不同的表中;
- 如果父类中有虚函数被子类重写,那么不管父类中虚函数访问权限是publib、private、protected,都会在虚函数表中进行覆盖(基类指针可以访问派生类private函数);
- 父类自身的虚函数会放在第一张虚函数表中。
3、对象的内存分配
这是第1点中遗留了一个问题,来看下第1点中对象fbase的内存分布。
从中可以看出其实对象的内存分布应该是:
父类的虚函数表头指针4个字节(看编译系统,32系统是4个字节)+成员变量 + ... + 自身的成员变量
做成表的形式如下:
至此,对象的虚函数使用问题基本是解释完了,通过上面的总结出总的内存分布图如下:
注意:
- 如果对象是通过new创建的,那么对象是分配在堆上面的,就如图所示。但是,如果对象只是普通创建,那应该是分配在栈上面,那这个图就需要稍微改动下。
- 虚函数表中存放的都是函数指针,函数段还是位于内存中的代码段。
- C++中虚函数详解二
- 虚函数详解(C++)
- C中在函数中定义函数(二)
- C语言中memset函数详解
- C语言中fopen函数用法详解
- C语言中string函数详解
- C语言中memset函数详解
- C语言中scanf函数的详解
- C语言中strtol函数详解
- objective-c中fopen函数用法详解
- C/C++中函数参数传递详解
- C语言中memset函数详解
- C/C++中函数参数传递详解
- C语言中memset函数详解
- C/C++中函数参数传递详解
- C/C++中函数参数传递详解
- C语言中memset函数详解
- C语言中memset函数详解
- Python之路---一些小操作
- 图片仿TabLayout Indicator , 自定义Indicator
- 对于session.getAttribute("user");不能用问题的解决办法
- Linux系统监视端口上收到的数据
- 系统状态栏设置背景颜色和APP标题栏一致(非全屏)
- C++中虚函数详解二
- React-native 响应用户的点击事件Touchablexxx
- python openpyxl 操作 excel ==> 简单读取(持续完善...)
- 4用于cifar10的卷积神经网络-4.11优化器GradientDescenOptimizer的汇总结果分析
- logstash配置之自动重载配置文件
- 使用gitlab做git flow及代码审查
- 使用leakcanary分析程序中的内存泄露
- Java中的序列化(Serializable)
- 理解JavaScript 执行机制及异步回调(setTimeout/setInterval/Promise)