• 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改变。

  • 相关阅读:
    对double数据类型的数据保留两位小数,并且进行四舍五入
    div位置设置
    每天一算法 -- (排序算法总结)
    SQL行转列
    设计模式的六大原则
    每天一算法 -- (插入排序)
    每天一算法 -- (选择排序)
    通用扩展函数--类型转换
    wcf和webservice
    Cookie的介绍及使用
  • 原文地址:https://www.cnblogs.com/minglee/p/8657325.html
Copyright © 2020-2023  润新知