c++多态
原文:【C++】多态(举例+详解,超级详细)_c++多态-CSDN博客
c++的多态分为两种,一种是静态多态,一种是动态多态。我们一般说的虚函数的方法其实是属于动态多态。
继承中构成动态多态的两个条件:
1. 通过基类的指针或引用调用虚函数
2. 被调用的函数是虚函数
(要注意与隐藏的关系,隐藏不要求虚函数和参数,且基类的同名函数仍然存在,可以通过基类名字进行访问;而多态或说重写就只有一个函数了,即派生类重写的函数,虚表中也不会存基类的虚函数,只存派生类重写的函数)
虚函数:virtual修饰的函数
重写基类虚函数时,派生类的虚函数函数即使不加virtual也构成重写,仍旧保持虚函数属性。
虚函数重写的两个特例:
1. 派生类虚函数返回类型是基类返回类型的子类,称为协变
class A {}; class B : public A {}; class Person { public: virtual A* f() { return new A; } }; class Student : public Person { public: virtual B* f() { return new B; } };
2. 析构函数
基类的析构函数为虚函数,派生类的析构函数一定构成重写(即使名字不同)。编译器对对析构函数同一处理成destructor。
需要注意的是基类的析构函数需要声明为虚函数,否则释放基类指针(指向派生类)是只会调用基类的析构函数。
class A { public: virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; } virtual void test() { func(); } }; class B : public A { public: void func(int val = 0) { cout << "B->" << val << std::endl; } }; int main(int argc, char* argv[]) { B*p = new B; p->test(); return 0; }
注意这里输出的是B->1,因为虚函数时接口继承,实现重写,func的参数还是val=1。
override 检查派生类虚函数是否重写了基类的某个函数,如果没有就编译报错;final 修饰虚函数,表示该虚函数不能被重写。
class Car { public: virtual void Drive() final {} }; class Benz :public Car { public: virtual void Drive() { cout << "Benz-舒适" << endl; } };
class Car { public: virtual void Drive() {} }; class Benz :public Car { public: virtual void Drive() override { cout << "Benz-舒适" << endl; } };
纯虚函数:
基类中声明的虚函数,在后面加上“=0”,基类中没有实现,需要派生类去实现,这种函数叫纯虚函数。包含纯虚函数的类叫抽象类,抽象类不能实例化对象,需要通过派生类实现纯虚函数后在派生类中实例化。
这里还要讲一下底层实现:
class A { public: virtual void vfunc1(); virtual void vfunc2(); void func1(); void func2(); private: int m_data1, m_data2; }; class B : public A { public: virtual void vfunc1(); void func1(); private: int m_data3; }; class C: public B { public: virtual void vfunc2(); void func2(); private: int m_data1, m_data4; };

由于这三个类都有虚函数,故编译器为每个类都创建了一个虚表,即类A的虚表(A vtbl),类B的虚表(B vtbl),类C的虚表(C vtbl)。类A,类B,类C的对象都拥有一个虚表指针,*__vptr,用来指向自己所属类的虚表。
类A包括两个虚函数,故A vtbl包含两个指针,分别指向A::vfunc1()和A::vfunc2()。
类B继承于类A,故类B可以调用类A的函数,但由于类B重写了B::vfunc1()函数,故B vtbl的两个指针分别指向B::vfunc1()和A::vfunc2()。
类C继承于类B,故类C可以调用类B的函数,但由于类C重写了C::vfunc2()函数,故C vtbl的两个指针分别指向B::vfunc1()(指向继承的最近的一个类的函数)和C::vfunc2()。
虽然图3看起来有点复杂,但是只要抓住“对象的虚表指针用来指向自己所属类的虚表,虚表中的指针会指向其继承的最近的一个类的虚函数”这个特点,便可以快速将这几个类的对象模型在自己的脑海中描绘出来。
非虚函数的调用不用经过虚表,故不需要虚表中的指针指向这些函数。
此外还有静态多态:
1. 函数重载
2. 模板
特点是编译时绑定对应的方法,动态多态是运行时绑定方法。静态多态,编译时期的多态,编译器可以根据函数实参的类型确定要调用的函数,或确定模板类的具体类型。其中函数重载又包括普通函数的重载和成员函数的重载,与参数个数以及类型有关,与参数名和返回类型无关。


浙公网安备 33010602011771号