多态

多态的条件

  • 必须存在继承关系
  • 继承关系必须有同名虚函数
  • 存在基类类型的指针或者引用,通过该指针或引用调用虚函数。

虚函数声明

  • 纯虚函数声明如下: virtual void funtion()=0 纯虚函数一定没有定义,纯虚函数用来规范派生类的行为,即接口。包含纯虚函数的类是抽象类,抽象类不能定义实例,但可以声明指向实现该抽象类的具体类的指针或引用。
  • 虚函数声明如下:virtual ReturnType FunctionName(Parameter) 虚函数必须实现。

不建议用父类指针指向子类,尤其是父类析构函数没有声明为虚函数,采用父类指针,其生存周期结束时会自动调用父类的析构函数,而不会调用子类析构函数,子类未完全析构,造成内存泄漏。

实现多态父类析构函数定义为虚函数

  1. 构造函数顺序:先执行父类的构造函数,再执行子类的构造函数
  2. 析构函数顺序:先执行子类的析构函数,再执行父类的析构函数

  3. 父类析构函数没有定义为virtual时

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class mParent
    {
    public:
    mParent()
    {
    cout << "p gouzhao" << endl;
    }
    ~mParent()
    {
    cout << "p xigou" << endl;
    }
    virtual void print()
    {
    cout << "this is parent" << endl;
    }
    };

子类A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A : public mParent
{
public:
A()
{
cout << "A gouxhao" << endl;
}

~A()
{
cout << "A xigou" << endl;
}

virtual void print()
{
cout << "this is A" << endl;
}
};

使用多态

1
2
3
parent * p = new A;
p->F();
delete p;

结果是:
jieguo
此时delete p 没有使用子类A的析构函数,如果A里有裸指针,则会造成内存泄漏

  1. 父类析构函数定义为virtual时
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class mParent
    {
    public:
    mParent()
    {
    cout << "p gouzhao" << endl;
    }
    virtual ~mParent()
    {
    cout << "p xigou" << endl;
    }
    virtual void print()
    {
    cout << "this is parent" << endl;
    }
    };

结果是:
jieguo
此时delete p 使用子类A的析构函数.

构造函数不能声明为虚函数的原因

  1. 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象 的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类,无法确定
  2. 虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初 始化,将无法进行。
    虚函数的意思就是开启动态绑定,程序会根据对象的动态类型来选择要调用的方法。然而在构造函数运行的时候,这个对象的动态类型还不完整,没有办法确定它到底是什么类型,故构造函数不能动态绑定。

静态多态

在编译时实现多态

动态多态

在运行时实现多态