C++深入学习-指针,引用与多态

    在上一篇笔记中,我记录了对C++对象模型的理解,包括支持多态的对象模型结构。那么多态发生的最直接最基本的条件为什么必须是指针(Pointer)和引用(Reference)呢?
    这个问题可以追溯到刚接触C++时,教材上就是这么写的:经由指针和引用完成的函数调用会被动态完成,对象则不可以。我们在这里统一以指针调用来说明这个问题(引用本质通常是由指针实现,我们视为const 指针)。先看下列代码:

class CZooAnimal {
public:
    CZooAnimal() {}
    virtual ~CZooAnimal() {}
public:
    virtual void Run() { cout << "CZooAnimal::Run" << endl; }
protected:
    int m_loc;
    std::string m_name;
};
class CZooBear : public CZooAnimal {
public:
    CZooBear() {}
    ~CZooBear() {}
public:
    void Run() { cout << "CZooBear::Run" << endl; }
    virtual void Eat() {}
protected:
    enum EBearType { ebNull = 0 };
protected:
    EBearType m_type;
    int m_high;
};

    代码比较简单,一个CZooAnimal类和派生类CZooBear,根据上一篇内容就会知道CZooBear对象的结构如下图:
C++深入学习-指针,引用与多态
    在结构图中定义了一个CZooBear obj,假设obj在内存中的地址是10000,那么一个指向obj的指针CZooBear *pObj指向的就是10000这个地址。然后,定义一个CZooAnimal *pAObj也指向这个地址,当执行pAobj->Run()的时候,实际运行的是CZooBear::Run()这个函数,这就是我们常用的运行时多态。但是,pAObj是访问不到m_high的,pObj则可以,为什么?因为“指针类型”已经规定了编译器如何解释特定地址中的内存内容及大小(范围/界限),pAObj注定是与m_high无缘相见的。如果不相信命运,真要实现,那就需要一番努力了(好有哲理)。
    怎么去做呢?通过cast(转换),无论dynamic_cast还是static_cast,都是要获得类似pObj的存在。cast(转化)其实就是一种编译器指令,这个指令没有改变指针包含的真正地址,它只影响了“特定地址中的内存内容及大小”的解释方式。它改变了pAObj的视野范围(指针类型),使其可以看得更多,做得更多。
    好了,有了上面的铺垫,来说一下普通对象为什么就不能完成动态的调用。

CZooAnimal aObj = obj;
aObj.Run();

    这样写,aObj 就是努力在模仿obj,想表现为obj。但是,怎么说来着:一直被模仿,从未被超越 ????
    哪怕再努力一下,这样写:

CZooAnimal *pAAObj = &aObj;
pAAObj->Run();

也调用不到CZooBear::Run(),也就是无论你怎么转换,再也回不去了。
    真正的原因是:编译器在CZooAnimal aObj初始化或aObj = objfuz赋值的时候,首先确保了aObj有一个或一个以上的vptrs,然后就是确保这个vptrs不会随着初始化而改变。这两步操作把我们所有的幻想给终结了,并且CZooAnimal aObj = obj的过程发生了切割(sliced),切割完成的aObj保持了纯正的CZooAnimal 特性,一个新的CZooAnimal在内存中诞生了,就是这样。
    借用书中的话总结一下:一个指针(Pointer)或引用(Reference)之所以支持运行时多态,是因为它们并不引发任何"与类型有关的内存操作",会改变的,只是所指向内存的“内容和大小解释方式”而已。