1. 程式人生 > >c++對象模型探索(一)

c++對象模型探索(一)

定義 發現 什麽 對象模型 是個 OS 並且 std out

粗略閱讀了《深度探索c++對象模型》一書後,對c++對象底層的內存布局有了一些了解,但同時,也產生了一些疑惑:

1、將子類指針用dynamic_cast轉成父類指針之後,其虛表指針會相應變化麽?

2、父類轉子類呢?

以下是驗證疑惑的代碼:

#include <iostream>

class A { 
public:
    virtual void func() {
        std::cout << "a: " << a << std::endl;
    }   
    int a = 1;
};

class B : public
A { public: virtual void func() { std::cout << "b: " << b << std::endl; } int b = 2; }; int main() { A* pa1 = new B(); B* pb1 = (B*)pa1; B* pb2 = new B(); A* pa2 = dynamic_cast<A*>(pb2); A* pa3 = new A(); B* pb3 = dynamic_cast<B*>(pa3); pa1
->func(); pb1->func(); pa2->func(); pb2->func(); pa3->func(); pb3->func(); return 0; }

編譯:g++ object.cpp -o main --std=c++11 -g

執行結果:

$ ./main 
b: 2
b: 2
b: 2
b: 2
a: 1
Segmentation fault

在pb3調用func的時候,發生了一個段錯誤。這個不大符合我的預期,查詢了下dynamic_cast的用法,發現:

dynamic_cast
只用於對象的指針和引用。當用於多態類型時,它允許任意的隱式類型轉換以及相反過程。不過,與static_cast不同,在後一種情況裏(註:即隱式轉換的相反過程),dynamic_cast會檢查操作是否有效。也就是說,它會檢查轉換是否會返回一個被請求的有效的完整對象。 檢測在運行時進行。如果被轉換的指針不是一個被請求的有效完整的對象指針,返回值為NULL.

如上所說,當dynamic_cast檢測到轉換不成功的時候,返回的是個NULL指針,因此就不難解釋,為什麽會發生段錯誤了。

另外,A* pa2是從B*轉換過來的,但其實調用的還是類型B的方法,由此可以推斷,類型的虛表指針在類型分配內存的時候就確定了,dynamic_cast的轉換並不能將虛表指針重新賦值。

重新調整了下main函數代碼:

 1 int main() {
 2     A* pa1 = new B();
 3     B* pb1 = (B*)pa1;
 4 
 5     B* pb2 = new B();
 6     A* pa2 = dynamic_cast<A*>(pb2);
 7     A* pa21 = (A*)pb2;
 8     A* pa22 = static_cast<A*>(pb2);
 9 
10     A* pa3 = new A();
11     B* pb3 = static_cast<B*>(pa3);
12 
13     pa1->func();
14     pb1->func();
15     pb2->func();
16     pa2->func();
17     pa21->func();
18     pa22->func();
19     pa3->func();
20     pb3->func();
21     return 0;
22 }

運行結果如下:

$ ./main 
b: 2
b: 2
b: 2
b: 2
b: 2
b: 2
a: 1
a: 1

可見,不管是強轉、static_cast還是dynamic_cast,都不會影響虛表指針,可以通過GDB來驗證下:

(gdb) p *pa1
$1 = {_vptr.A = 0x400d10 <vtable for B+16>, a = 1}
(gdb) p *pb1
$2 = {<A> = {_vptr.A = 0x400d10 <vtable for B+16>, a = 1}, b = 2}
(gdb) p *pb2
$3 = {<A> = {_vptr.A = 0x400d10 <vtable for B+16>, a = 1}, b = 2}
(gdb) p *pa2
$4 = {_vptr.A = 0x400d10 <vtable for B+16>, a = 1}
(gdb) p *pa21
$5 = {_vptr.A = 0x400d10 <vtable for B+16>, a = 1}
(gdb) p *pa22
$6 = {_vptr.A = 0x400d10 <vtable for B+16>, a = 1}
(gdb) p *pa3
$7 = {_vptr.A = 0x400d30 <vtable for A+16>, a = 1}
(gdb) p *pb3
$8 = {<A> = {_vptr.A = 0x400d30 <vtable for A+16>, a = 1}, b = 0}

可以看到用new B() 方式定義的對象,虛表指針都是指向 B類的虛表指針,用new A()方式定義的對象,虛表指針都指向了A。

由此可以看出,對象的虛表指針,在定義的時候,就確定了,並且不會隨著強轉、static_cast或dynamic_cast改變。

c++對象模型探索(一)