[C++]多态与虚函数
[C++]多态与虚函数
多态
多态的字面意思是,具有多种形态,体现在程序中就是:同一个函数的行为随上下文而异。C++通过静态绑定和动态绑定实现多态。
其中,静态绑定是在编译时就确定了要调用的函数,编译器会根据函数名和参数来决定,但是对于虚函数,只能在程序运行时进行选择。
对于面向对象中的多态性,一般只考虑动态绑定,C++通过虚函数来实现。
虚函数
在类中声明一个虚函数,使用关键字“virtual”,使得子类可以重写(override)这个函数:
class Person
{
protected:
string name;
public:
Person(string n) : name(n) {}
virtual void Say()
{
cout << "I'm a person.\n";
};
};
class Student : Person
{
public:
int id;
Student(string n, int i) : public Person(n), id(i) {}
void Say() override
{
cout << "I'm a student.\n";
};
};
当然,也可以声明纯虚函数,此时该类为抽象类,不能被实例化
virtual void f() = 0;
构造函数不能是虚函数;析构函数应该是虚函数,通常应给基类提供一个虚析构函数,即使它并不需要析构函数。
向上类型转换(upcasting)
C++一般不允许将A类的地址赋值给B类指针,也不允许不同类型间的引用。但对于继承关系,C++的指针和引用运行父类指针指向子类对象,而不需要作显式类型转换:
Student s("Alice", 1);
Person *p = &s;
Person &r = s;
这被称为向上类型转换(upcasting),而将子类指针指向父类地址,则是向下类型转换,显然需要显示类型转换:
Student* ps = static_cast<Student*>(&p1);
有了upcasting,就需要在运行时确定类型。
虚函数表
编译器处理虚函数时,会为类型添加一个隐藏的成员数组,称为虚函数表(virtual function table,vtbl),而虚函数表中存储了声明的所有虚函数的地址。
如果子类override了父类的一个虚函数,那么它的地址就是新的,否则就跟父类中的地址一样。
调用虚函数时,程序将查看存储在对象中的vtbl地址,然后转向相应的函数地址表,找到对应的地址,并执行具有该地址的函数。
可以参考图片:


浙公网安备 33010602011771号