静态多态——函数重载
动态多态——虚函数
#include <iostream>
using namespace std;
class father {
public:
virtual void A() {
cout << "father" << endl;
}
void B() {
A();
}
};
class son: public father {
public:
void A() {
cout << "son" << endl;
}
};
int main() {
son s;
cout << "s.B()" << endl;
s.B();
father f = s;
cout << endl << "f.B()" << endl;
f.B();
cout << endl << "s.A()" << endl;
s.A();
cout << "s.father::A()" << endl;
s.father::A();
cout << endl << "s.father::B()" << endl;
s.father::B(); //在这里等价于s.B()
cout << "(father)s.B()" << endl;
((father)s).B();
father *pf = new son();
cout << endl << "pf->B()" << endl;
pf->B();
return 0;
}
执行结果:
父类和子类出现同名函数称为隐藏。
- 父类对象.函数名(...); //调用父类的函数
- 子类对象.函数名(...); //调用子类的函数
- 子类对象.父类名::函数名(...);//子类调用从父类继承来的函数。
父类和子类出现同名虚函数称为覆盖
- 父类指针=new 子类名(...);父类指针->函数名(...);//调用子类的虚函数。
通过父类指针操作子类对象的成员函数的时候是没有问题的,可是在销毁对象内存的时候则只是执行了父类的析构函数,子类的析构函数却没有执行,这会导致内存泄漏。为解决内存泄漏问题,引入虚析构函数。
虚析构函数的实现原理
[:->虚析构函数的特点:
- 当我们在父类中通过virtual修饰析构函数之后,通过父类指针指向子类对象,通过delete接父类指针就可以释放掉子类对象
[:->理论前提:
- 执行完子类的析构函数就会执行父类的析构函数
注:
1)普通函数不能是虚函数,这个函数必须是某一个类的成员函数,不可以是一个全局函数。
2)友元函数不能做虚函数。
3)静态成员函数不能做虚函数,static成员函数是和类同生共处的,不属于任何对象,使用virtual将导致错误。
4)内联函数不能是虚函数,如果内联函数被virtual修饰,计算机会忽略inline使它变成纯粹的虚函数。
(inline函数在编译时被展开,在调用处将整个函数替换为代码块,省去了函数跳转的时间,提高了SD,减少函数调用的开销,虚函数是为了继承后对象能够准确的调用自己的函数,执行相应的动作。
主要的原因是:inline函数在编译时被展开,用函数体去替换函数,而virtual是在运行期间才能动态绑定的,这也决定了inline函数不可能为虚函数。(inline函数体现的是一种编译机制,而virtual体现的是一种动态运行机制)
5)构造函数和拷贝构造函数不能是虚函数,但析构函数可以是虚函数。
纯虚函数:
没有函数体,同时在定义的时候要在后面加"=0"。
virtual void func() = 0;
含有纯虚函数的类被称为抽象类,抽象类不允许实例化对象。
仅含有纯虚函数的类被称为接口类,接口类自身不能实例化,接口类的方法定义/实现只能由接口类的子类完成。
对于C++,接口类一般具有以下特征:
1)最好不要有成员变量,但可以有静态常量(static const或enum)
2)接口方法都要声明称纯虚函数
3)要有虚析构函数,最好提供默认实现(如果接口类的析构函数是纯虚析构函数的话,接口类的子类将被迫必须提供析构函数的实现,这样对接口类的子类不友好)
4)不声明构造函数。
https://www.cnblogs.com/jin521/p/5602190.html c++深入理解虚函数
https://blog.csdn.net/a15929748502/article/details/80920132 c++虚函数的作用及实现原理
https://blog.csdn.net/netyeaxi/article/details/80724557 C++如何正确的定义一个接口类
https://blog.csdn.net/netyeaxi/article/details/80887646 C++如何正确使用接口类