【C++】多态性和虚函数

多态及其相关概念理解

●早捆绑/晚捆绑

●虚函数

●虚析构函数

●纯虚函数

●抽象类

多态

C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象所属的不同类来调用类中实现的相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数

早捆绑(静态多态)

程序在编译阶段(运行之前),根据参数个数确定调用哪个函数

class Rect       //矩形类,实现了两种计算面积的方法,其函数名相同,参数个数不同
{
public:
    int calcArea(int width);
    int calcArea(int width,int height);
};
int main()
{
    Rect.rect;
    rect.calcArea(10);//调用第一个函数
    rect.calcArea(10,20);//调用第二个函数
    return 0;
}

晚捆绑(动态多态)

程序在运行期间,根据传入的参数(不同的派生类),调用各自类中实现的同名方法

例:有一个计算面积的形状基类,圆形和矩形类派生自形状类,圆形与矩形的类各有自己的计算面积的方法,当给圆形计算面积时使用圆形面积的计算公式,给矩形计算面积时使用矩形面积的计算公式(可见动态多态是以封装和继承为基础的

//形状类
class Shape   
{
public:
    double calcArea()
    {
        cout<<"calcArea"<<endl;
        return 0;
    }
};

//继承自形状类的圆形类
class Circle:public Shape      
{
......
double Circle::calcArea()//圆形面积公式
{
    return 3.14*m_dR*m_dR;
}

//继承自形状类的矩形类
class Rect:public Shape       
{
......
double Rect::calcArea()//矩形面积公式
{
    return m_dWidth*m_dHeight;
}

int main()
{
    Shape *shape1=new Circle(4.0);//基类Shape指针通过new运算符指向派生类Circle对象
    Shape *shape2=new Rect(3.0,5.0);//基类Shape指针通过new运算符指向派生类Rect对象
    shape1->calcArea();//调用圆形面积公式
    shape2->calcArea();//调用矩形面积公式
    ......
    return 0;
}

虚函数

虚函数:基类中使用virtual关键字修饰的成员函数、

class Shape
{
public:
    virtual double calcArea(){...}//虚函数
    ....                                     
private:
    ....
};

虚函数的主要作用是建立抽象模型,从而可以达到方便扩展系统的目的。纯虚函数是指被标明为不具体实现的虚函数,是一种特殊的虚函数

虚析构函数

基类中使用virtual关键字修饰的析构函数

●函数指针
指针指向对象称为对象指针,指针除了指向对象还可以指向函数,函数的本质就是一段二进制代码,我们可以通过指针指向这段代码的开头,计算机就会从这个开头一直往下执行,直到函数结束,并且通过指令返回回来。函数的指针与普通的指针本质上是一样的,也是由四个基本的内存单元组成,存储着内存的地址,这个地址就是函数的首地址

●基类指针
继承同一基类的不同派生对象,对来自基类的同一消息执行了不同的行为,这就是多态,而接受同一消息的实现就是基于基类指针

●虚析构函数的使用场景
使用delete()语句销毁对象内存时,如果传入的是基类的指针,那么只是执行了基类的析构函数,派生类的析构函数没有执行,这会导致内存泄漏。面对这种情况则需要引入虚析构函数

class Shape
{
public:
    ....
    virtual ~Shape();//基类虚析构函数
private:
    ....
};

class Circle:public Shape
{
public:
    virtual ~Circle();//派生类虚析构函数,此处virtual可以不写,系统将会自动添加,建议写上
    ....
};


●实现原理(重要)
如果基类当中定义了虚析构函数,那么基类的虚函数表当中就会产生一个指向基类虚析构函数的函数指针,派生类虚函数表当中也会产生一个指向派生类的虚析构函数的函数指针(将原先继承的基类虚析构函数指针覆盖)
使用基类的指针指向派生类的对象(基类指针通过new运算符指向派生类对象,eg: Base* pB = new Derived(); ),在delete方法中传入基类指针,就会通过指向的派生类的对象找到派生类的虚析构函数指针,从而在虚函数表中找到派生类的虚析构函数,使得派生类的析构函数得以执行,接着系统会自动执行基类的虚析构函数

纯虚函数

在基类中仅仅给出声明,不对虚函数实现定义,而是在派生类中实现

其关联到了设计模式中“接口”的概念

纯虚函数的语法
        -将成员函数声明为virtual
        -后面加上 = 0
        -该函数没有函数体

class Shape
{
public:
    virtual  double calcArea()//虚函数
    {....}

    virtual  double calcPerimeter()=0;//纯虚函数
};

抽象类

含有纯虚函数的类叫做抽象类(纯虚类),抽象类是一种特殊的类,它是为了抽象和设计的目的而建立的,用于充当“接口函数”(同JAVA中的Interface)。凡是遵循此规范的类,都必须实现指定的所有函数接口
抽象类不能被实例化,抽象类子类只有把抽象类当中的所有的纯虚函数都做了实现才可以实例化对象(否则子类也是一个抽象类)

eg:
  CmdHandler ch;                    // 编译错误!!
  CmdHandler *p = new CmdHandler();  // 编译错误!!

参考资料:

c++ 深入理解虚函数

C++ 多态详解

C++ 虚函数表解析

C++的抽象类详解

为什么要用基类指针指向派生类对象?

 

猜你喜欢

转载自blog.csdn.net/qq_28038873/article/details/81604319