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 A
22 {
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 END0
3 0xf
4 0x1f
5 8
6 12
flag = 1;
復制代碼
1 ~B
2 ~A
3 END4460356
4 0xf
5 0x1f
6 8
7 12
復制代碼
flag = 2;
復制代碼
~B
~A
END1818846781
0x2c29a0
0x1f
8
12
復制代碼
分析:
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,我認為是內存沒有更新的原因。
結論:與第一篇裡說的那樣,虛析構函數的第“二”個是釋放內存用的,第一個仍然不知道干嘛的。其它的與文章最開始介紹的兩個連接裡說的一樣,子類的虛函數會覆蓋父類對應虛函數在虛函數表中的位置。