多态的条件
- 必须存在继承关系
- 继承关系必须有同名虚函数
- 存在基类类型的指针或者引用,通过该指针或引用调用虚函数。
虚函数声明
- 纯虚函数声明如下:
virtual void funtion()=0
纯虚函数一定没有定义,纯虚函数用来规范派生类的行为,即接口。包含纯虚函数的类是抽象类,抽象类不能定义实例,但可以声明指向实现该抽象类的具体类的指针或引用。 - 虚函数声明如下:
virtual ReturnType FunctionName(Parameter)
虚函数必须实现。
不建议用父类指针指向子类,尤其是父类析构函数没有声明为虚函数,采用父类指针,其生存周期结束时会自动调用父类的析构函数,而不会调用子类析构函数,子类未完全析构,造成内存泄漏。
实现多态父类析构函数定义为虚函数
- 构造函数顺序:先执行父类的构造函数,再执行子类的构造函数
析构函数顺序:先执行子类的析构函数,再执行父类的析构函数
父类析构函数没有定义为virtual时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class mParent
{
public:
mParent()
{
cout << "p gouzhao" << endl;
}
~mParent()
{
cout << "p xigou" << endl;
}
virtual void print()
{
cout << "this is parent" << endl;
}
};
子类A1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class A : public mParent
{
public:
A()
{
cout << "A gouxhao" << endl;
}
~A()
{
cout << "A xigou" << endl;
}
virtual void print()
{
cout << "this is A" << endl;
}
};
使用多态1
2
3parent * p = new A;
p->F();
delete p;
结果是:
此时delete p
没有使用子类A的析构函数,如果A里有裸指针,则会造成内存泄漏
- 父类析构函数定义为virtual时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class mParent
{
public:
mParent()
{
cout << "p gouzhao" << endl;
}
virtual ~mParent()
{
cout << "p xigou" << endl;
}
virtual void print()
{
cout << "this is parent" << endl;
}
};
结果是:
此时delete p
使用子类A的析构函数.
构造函数不能声明为虚函数的原因
- 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象 的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类,无法确定
- 虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初 始化,将无法进行。
虚函数的意思就是开启动态绑定,程序会根据对象的动态类型来选择要调用的方法。然而在构造函数运行的时候,这个对象的动态类型还不完整,没有办法确定它到底是什么类型,故构造函数不能动态绑定。
静态多态
在编译时实现多态
动态多态
在运行时实现多态