多态原理
当类存在虚函数时,编译器会为该类维护一个表,这个表就是虚函数表(vtbl),里面存放了该类虚函数的函数指针。在构造类的时候增加一个虚表指针(vptr)指向对应的虚函数表。在类执行成员函数时候,先判断该函数是否是虚函数,如果不是虚函数则直接执行对应的方法,如果是虚函数则从虚函数表中找到应该调用的函数。
- vptr的初始化是在类的构造时候完成。而在进行父类构造的时候,vptr指向的是父类vtbl。执行完子类的构造函数后,vptr在指向子类的btbl。
- 父类指针(引用)指向子类进行多态调用时候,编译器并不关系是子类还是父类,而是通过vptr指向的btbl来找到对应的函数指针,并调用函数。
- vtbl由编译器来维护
重载、覆盖、隐藏的区别和执行方式
4.1成员函数被重载的特征
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
4.2“覆盖”是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。
4.3“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,特征是:
(1)如果派生类的函数与基类的函数同名,但是参数不同,此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,但是参数相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
小结:说白了就是如果派生类和基类的函数名和参数都相同,属于覆盖,这是可以理解的吧,完全一样当然要覆盖了;如果只是函数名相同,参数并不相同,则属于隐藏。
示例
class Parent {
public:
int x;
Parent(int x) {
this->x = x;
}
void print() {
cout << "Parent " << x << endl;
}
};
class Children : public Parent {
public:
Children(int x) : Parent(x) {
}
void print() {
cout << "Children " << x << endl;
}
};
int main() {
Parent *parent = NULL;
Parent p(1);
Children c(10);
parent = &p;
parent->print();
parent = &c;
parent->print();
}
测试一运行结果,发现虽然父类指针或引用指向了子类,但是在调用方法时候还是调用的父类的方法
Parent 1
Parent 10
测试二,对print方法加上virtual关键字,这时才体现出多态
Parent 1
Children 10