1. vtable
在构造子类object的时候,首先调用基类构造函数,如果基类中有virtual function,那么基类构造函数会建一个虚函数表( vtable ) 。如果子类中override了基类中的函数,那么会用这个函数地址覆盖原来vtable中的entry。
这样在使用
SubClass *s = new SubClass();
BaseClass *base = s;
时,base->foo() 就可以调用到正确的子类中的函数(foo()在BaseClass中为virtual, 并被SubClass中的foo() override掉)。
2. object slicing
看下面的代码
#include <iostream>
#include<string>
using namespace std;
class Pet {
public:
int age_;
Pet(int age): age_(age) {}
virtual string getDescription() const {
return "This is Pet class";
}
};
class Dog : public Pet {
public:
Dog(int age) : Pet(age) {}
virtual string getDescription() const {
return "This is Dog class";
}
};
int main() {
Dog d(2);
Pet pet(0);
pet = d;
cout << pet.age_ << endl; //should be 2
Pet *pp = &d;
cout<<pp->getDescription(); //should call func in Dog
Pet p = d;
cout << p.getDescription(); //should call func in Pet
return 0;
}
当我们用Pet p = d,或传参时pass by value(instead of pass by reference)的时候,就会导致object slicing。因为copy constructor或operator=是这样的:
class MyClass {
int x;
char c;
std::string s;
};
MyClass::MyClass(const MyClass& other) : x(other.x), c(other.c), s(other.s) {}
那么vtable会怎么变化呢?我猜,它会直接读p的vtable(复制构造函数直接构造出来的,而不是用s.vtable覆盖p.vtable,因为它不知道怎么对应地找)