首页 > 代码库 > c++对象内存模型之虚析构函数篇(2)
c++对象内存模型之虚析构函数篇(2)
现在讨论第二种情况:
(第一种情况传送门,单独一个类,有虚析构函数,虚函数)
(2)有继承关系,单一继承,父类无虚析构函数,子类有(子类没有就没必要说了)
这种情况让我相当晕,照例先贴代码 :
1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 int ia; 7 public: 8 A ():ia(15) 9 { 10 } 11 ~A ()12 {13 cout << "~A" << endl;14 } 15 virtual void f()16 {17 cout << "A:f()" <<endl;18 } 19 };20 21 class B : public A22 {23 int ib;24 public :25 B():ib(31){}26 virtual ~B()27 {28 cout << "~B" << endl; 29 } 30 virtual void f()31 {32 cout << "B:f()" << endl; 33 }34 35 };36 37 typedef void (*F)();38 39 int main()40 {41 42 F pf = NULL;43 B *b = new B();44 int **p = (int **)b;45 46 int flag = 0;47 pf = (F)p[0][flag];48 pf();49 50 cout << "END"<<p[0][3] << endl;51 cout << p[1] << endl;52 cout << p[2] << endl;53 54 cout << sizeof(A) << endl;55 cout << sizeof(B) << endl;56 }
输出如下:
flag = 0;
1 B:f()2 END03 0xf4 0x1f5 86 12
flag = 1;
1 ~B2 ~A3 END44603564 0xf5 0x1f6 87 12
flag = 2;
~B~AEND18188467810x2c29a00x1f812
分析:
flag=0时, B的虚函数f替代了B的虚函数f,打印输出B:f(),这个好理解。要注意的地方是,此时给flag=0,意味着,f在虚函数表的最前面,A没有虚析构函数。END后面的数也很正常。
flag=1时,输出为~B\n~A\n,从现象上来看,是先执行B的析构函数再执行A的析构函数,与常识相符。但END后面的数据与flag=0时的数据0不同,再加上B中的两个成员变量的值是对的,这个让我很难相信对象内存被释放。如果没有释放,END后面的数又很难解释。
flag=2时,输出为~B\n~A\n,成员变量输出已经算是乱码,至少说明内存被释放。ia是乱码,ib仍然是0x1f。从输出上来看,先后执行了B,A的析构函数,确认内存已被释放。由于ib仍是0x1f,我认为是内存没有更新的原因。
结论:与第一篇里说的那样,虚析构函数的第“二”个是释放内存用的,第一个仍然不知道干嘛的。其它的与文章最开始介绍的两个连接里说的一样,子类的虚函数会覆盖父类对应虚函数在虚函数表中的位置。
下面画个内存结构示意图:
A的内存结构请见第一篇文章,这里只画B的。
vptr是虚函数表的指针,图里的都好说,当然,这些全是根据现象猜的,目前来说没有去看权威资料的原因是为了真正理解,目前看自己能猜对多少。没人给我打分,怕毛。
注:类图可以用ArgoUML,内存图可以用win7自带的画图工具。
c++对象内存模型之虚析构函数篇(2)