本文目的
在c++的多态中,如果一个基类的虚函数被派生类重写,那么把基类的指针指向派生类,就能够通过基类调用被派生类重写的这个虚函数。(对于这点有疑问的,可以看我之前写的C++多态,虚函数,虚函数表,gcc下的单继承,多继承,虚继承的内存布局)。那么如果不用指针或引用会如何?
class Animal{
public:
virtual void eat()
{
cout<<"能吃什么就吃什么"<<endl;
}
virtual void run(){}
private:
int name;
};
class Dog : public Animal{
public:
virtual void eat(){
cout<<"吃狗粮"<<endl;
}
virtual void shout(){}
private:
int age;
};
int main()
{
Dog dog;
Animal&animal1 = dog;
Animal* animal2 = new Dog();
Ainmal animal3 = dog;
animal1.eat();
animal2->eat();
animal3.eat();
}
不用指针或引用,我们直接取值时,就不能调用多态。那么为什么呢?
内存切割
之所以能够多态是因为虚函数的内存布局,以之前代码为例,Dog类的内存布局在gcc下如图:
当我们执行以下代码时
Animal&animal1 = dog;
Animal* animal2 = new Dog();
将会产生内存切割,图中Dog类的Animal部分被animal1和animal2得到。从而实现多态机制。
Ainmal animal3 = dog;
那么为什么 这样调用就不能通过内存切割实现多态呢?
我在《深度探索C++对象模型》中找到了答案:
“一个pointer或一个reference之所以支持多态,是因为它们并不引发内存任何“与类型有关的内存委托操作; 会受到改变的。只有它们所指向内存的大小和解释方式 而已”
对这句话解释就是:
- 指针和引用类型只是要求了基地址和这种指针所指对象的内存大小,与对象的类型无关,相当于把指向的内存解释成指针或引用的类型。
- 而把一个派生类对象直接赋值给基类对象,就牵扯到对象的类型问题,编译器就会回避之前的的虚机制。从而无法实现多态。
尾语
《深度探索C++对象模型》有很多东西都比较解惑,不多第一遍读的时候,会有很多疑惑,也会遗漏很多细节,不过多读几遍,感觉就会很不一样,之前存在的问题,可能都会被解决。
以上