C++继承中的多态、虚函数、虚析构函数

多态

C++继承中的兼容原则中我们知道:父类指针可以直接指向子类对象,父类引用可以直接引用子类对象。当父类和子类有相同方法时调用的是父类方法,即是根据指针类型或引用类型来确定调用的方法类型的。如果我们想根据指针实际指向的对象类型(引用实际引用的类型)来决定调用的方法类型,需要把这个函数声明为虚函数,这就是多态。

#include <iostream>
using namespace std;

class A
{
public:
    void print()
    {
        cout<<"I am A"<<endl;
    }

    virtual void printv()
    {
        cout<<"I am vA"<<endl;
    }
};

class B:public A
{
public:
    void print()
    {
        cout<<"I am B"<<endl;
    }

    virtual void printv()
    {
        cout<<"I am vB"<<endl;
    }
};

void test(A *pA)
{
    pA->print();
    pA->printv();
}

int main()
{
    B b;
    test(&b);

    return 0;
}
View Code

执行结果:

I am A
I am vB

多态成立的条件 :

1 要有继承
2 要有虚函数重写
3 要有父类指针(父类引用)指向子类对象

 

虚析构函数

构造函数不能是虚函数。建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数。

析构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象

#include <iostream>
using namespace std;

class A
{
public:
    A()
    {
        p = new char[20];
        strcpy(p, "objA");
        cout<<"A()"<<endl;
    }
    virtual ~A()
    {
        delete [] p;
        cout<<"~A()"<<endl;
    }
private:
    char *p;
};

class B:public A
{
public:
    B()
    {
        p = new char[20];
        strcpy(p, "objB");
        cout<<"B()"<<endl;
    }
    ~B()
    {
        delete [] p;
        cout<<"~B()"<<endl;
    }
private:
    char *p;
};

class C:public B
{
public:
    C()
    {
        p = new char[20];
        strcpy(p, "objC");
        cout<<"C()"<<endl;
    }
    ~C()
    {
        delete [] p;
        cout<<"~C()"<<endl;
    }
private:
    char *p;
};

void test(A *pA)
{
    delete pA;
}

int main()
{
    C *pC = new C;

    //deletc pC;    直接通过子类指针释放对象,析构函数不需要声明为虚函数

    test(pC);    //通过父类指针释放对象,析构函数必须声明为虚函数,要不然会只释放父类的成员p

    return 0;
}
View Code

执行结果:

A()
B()
C()
~C()
~B()
~A()
View Code

 

多态的实现原理

当类中声明虚函数时,编译器会在类中生成一个虚函数表
虚函数表是一个存储类成员函数指针的数据结构;
虚函数表是由编译器自动生成与维护的;
virtual成员函数会被编译器放入虚函数表中;
存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针)。

 

当调用函数func时,编译器首先确定func是不是虚函数:

(1)func不是虚函数:编译器可以直接确定被调用的函数(静态链编,根据指针或引用类型确定)

(2)func是虚函数:编译器根据实际指向的对象的vptr指针指向的虚函数表查找func函数并调用(动态链编,运行时完成)

 

说明:
1. 通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需
要通过寻址操作才能确定真正应该调用的函数。 而普通成员函数是在编译时就确定了调用的函数。在效率上, 虚函数的效率要低很多。

2.出于效率考虑,没有必要将所有成员函数都声明为虚函数.

posted @ 2020-05-21 17:49  xd_xumaomao  阅读(411)  评论(0编辑  收藏  举报