从内存布局看多态性

通过一个实验来说明,代码如下:

#include <iostream>
 
using namespace std;
 
class A {
public:
    int x, y;
    virtual void func() { cout << "This is A" << endl; }
};
 
class B: public A {
public:
    int z;
    void func() { cout << "This is B" << endl; }
};
 
int main()
{
    B b;
    A *pa = &b;
    B *pb = &b;
 
    cout << "memory size = " << sizeof(*pa) << endl;
    cout << "memory size = " << sizeof(*pb) << endl;
 
    pa->func();
    pb->func();
 
    return 0;
}


运行结果:


指针pa和指针pb都指向对象b,但pb所涵盖的地址包含整个对象(x、y、z、vptr),而pa所涵盖的地址只包括基类A的子对象(x、y、vptr)。从两者涵盖的地址范围可以看出,想要通过pa来访问类B中的成员是不允许的(除非类型强制转换),例如数据成员z。但有一个例外,那就是虚函数。从运行结果可以看出指针pa和指针pb都访问了类B中的成员函数,这是因为pa是通过vptr找到这个函数的地址的。pa、pb所指范围包含一个公共的vptr,两者对函数func()的调用都必须通过这一个vptr。vptr相同,vptr所指向的virtual table相同,virtual table内虚函数的地址当然也就相同了,从而调用的是同一个函数。

但如果是针对对象的操作:
B b;        // 派生类对象
A a = b;    // 发生切割


经过这样的初始化之后就不能利用对象a进行多态操作了。a.func()始终调用类A中的func函数。至于切割时编译器到底对它做了什么?这会在以后的学习中详细说明。

环境:
Win7 + Code::Blocks

参考:
《深度探索C++对象模型》 P28-P35.

你可能感兴趣的:(从内存布局看多态性)