直接看示例代码
#include <iostream>
using namespace std;
class Father
{
public:
Father(){};
virtual void func()
{
cout<<"Father"<<endl;
}
void func2()
{ cout<<"Father_func2"<<endl;}
};
class Child:public Father
{
public:
Child(){};
virtual void func()
{
cout<<"Child"<<endl;
}
void func2()
{ cout<<"Child_func2"<<endl;}
};
int main()
{
Father f;
Child c;
f=c;
f.func2();//只能调用基类中函数,输出“Father_func2”
f.func();//通过对象名调用,输出“Father”
Father &fa=c;
fa.func();//通过引用调用,输出“Child”
Father *fat=&c;
fat->func();//通过指针调用,输出“Child”
return 0;
}
可以得出如下结论:
- 通过子类赋值给父类,然后调用虚函数,不会引发动态绑定。
- 通过子类赋值给父类,然后调用成员函数(无论是否为虚函数),如示例中f.func2()的方式,只能调用派生类中的基类成员,而不能访问派生类增加的成员(同名函数按基类和派生类区分开);
- 通过指针和引用调用虚函数,可以实现运行时多态,具体实现涉及虚函数指针vptr和虚函数表vtbl相关知识。
- 通过对象名调用虚函数,在编译阶段就能确定调用的是哪一个类的虚函数,所以属于静态关联,如果通过基类指针调用虚函数,在编译阶段无法从语句本身确定调用哪一个类的虚函数,只有在运行时,指针指向某一具体对象后,才能确定调用的是哪一个类的虚函数,故为动态关联;
tips:
- 动态联编是指在程序运行的时候,编译器才去判断调用哪个函数或如何执行程序。如if-else结构、switch结构等。
- 静态联编是指程序在编译阶段就确定了如何执行。