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

粗略阅读了《深度探索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改变。

原文地址:https://www.cnblogs.com/minglee/p/8657325.html