c++类内存布局的一些理解(1)

来源:互联网 发布:做平面图用什么软件 编辑:程序博客网 时间:2024/06/06 08:32

先看这段代码

//代码来自陈皓的博客http://blog.csdn.net/haoel/article/details/3081328class Base1 {public:int ibase1;Base1():ibase1(10) {}virtual void f() { cout << "Base1::f()" << endl; }virtual void g() { cout << "Base1::g()" << endl; }virtual void h() { cout << "Base1::h()" << endl; }};class Base2 {public:int ibase2;Base2():ibase2(20) {}virtual void f() { cout << "Base2::f()" << endl; }virtual void g() { cout << "Base2::g()" << endl; }virtual void h() { cout << "Base2::h()" << endl; }};class Base3 {public:int ibase3;Base3():ibase3(30) {}virtual void f() { cout << "Base3::f()" << endl; }virtual void g() { cout << "Base3::g()" << endl; }virtual void h() { cout << "Base3::h()" << endl; }};class Derive : public Base1, public Base2, public Base3 {public:int iderive;Derive():iderive(100) {}virtual void f() { cout << "Derive::f()" << endl; }virtual void g1() { cout << "Derive::g1()" << endl; }};

如果执行代码

Derive d;Base1* b1_ptr = &d;Base2* b2_ptr = &d;Base3* b3_ptr = &d;cout << b1_ptr << endl << b2_ptr << endl << b3_ptr << endl;

会得到结果

0012FF48
0012FF50
0012FF58

也就是说,虽然三个指针指向同一个对象,但是它们的值确实不同的!!!

这是因为,Base2的指针,不管指向Base2的对象还是子类Derive的对象,任何对这个指针的操作,都是按照Base2类的定义的内存布局来处理的。很明显,在一个Derive对象中Base2的内存布局在保存在Base1内存布局之后,并且有8字节的偏移(虚函数表和变量ibase1)。所以,编译做了一个隐式的转换(我恨这些编译器偷偷干的事),将各个指针指向它自己实际的内存布局。

为了符合这个要求,编译器在背后做了许多事情。例如上面这个例子,还有为什么dynamic_cast是必需的。下面解释dynamic_cast的原因:

对转换 Derive* d_ptr = dynamic_cast<Derive*>(b2_ptr) 来说,首先我们想象一下d_ptr的和b2_ptr的值应该是什么,b2_ptr的值已经有了,是0012FF50,而d_ptr的值则应该是0012FF48(因为Base1类的内存布局在Derive类的内存布局的顶端)。很明显,d_ptr和b2_ptr是有偏移量的,至于偏移量有多少,依赖于编译器的实现,只有编译器知道。因此Derive* d_ptr = b2_ptr是个严重的错误(虽然直观的讲b2_ptr确实指向一个Derive类实例)。

同样为了符合这个要求,虚函数的设计十分复杂。

个人认为,虚函数之所以这么设计的原因(这个得看陈皓的博客),就是要满足任意一个指针,不管指向什么,都要按照由它类定义所定义的内存布局来解释内存,从这个角度理解虚函数问题应该有点新意,有空再写

0 0
原创粉丝点击