多态

1.多态与虚函数

多态性是指同一个操作作用于不同的对象就会产生不同的响应。多态性分为静态多态性和动态多态性,其中函数重载和运算符重载属于静态多态性,虚函数属于动态多态性。

一旦一个函数被声明为虚函数,无论经历多少次派生,都会保持虚函数的特性,即使派生类中没有使用virtual关键字,其仍然是虚函数。

虚函数有以下注意点:

1)virtual应用于修饰public或protect成员函数。使用virtual修饰private成员函数时尽管不会报错,但没有意义。

2)如果派生类中没有对基类虚函数进行重定义,则它将继承基类中的虚函数。

3)友元不能是虚函数,因为友元不是类成员,只有成员才能是虚函数,但可以在友元内调用虚函数解决问题。虚函数可以是另一个类的友元。

2.虚函数的访问

1)基指针访问

/************************classdef.h********************/
#include <iostream.h>
using namespace std;
class   base
{
    public:
         virtual  void  disp()
         {
              cout<<"hello,base"<<endl;
          }
};

class  child:public  base
{ 
      public:
          void disp()
          {
                cout <<"hello,child"<<endl;
           }
};
/************************main1.c********************/
#include "classdef.h"
int main()
{
      base  obj_base;
      base *pBase = &obj_base;
      child  obj_child;
      child *pChild = &obj_child;
      pBase->disp();
      pChild->disp();
      pBase = pChild;                      //将派生类指针赋值给基类函数
      pBase->disp();                       //使用基类指针调用虚函数
      pChild = (child*)&obj_base;   //使用基类对象地址为派生类指针赋值
      pChild->disp();                      //使用派生类指针调用虚函数,只取决于赋值对象
      pChild->base::disp();            //使用类名加作用域限定符指明要调用的版本
      return 0;
}

输出结果:

hello,base
hello,child
hello,child
hello,base
hello,base

2)引用访问

/************************main2.c********************/
#include "classdef.h"
int main()
{
      base  obj_base;
      base& rBase1 = obj_base;
      child  obj_child;
      rBase1.disp();                       //基类引用调用虚函数,基类中的disp版本
      base&  rBase2 = obj_child;  
      rBase2.disp();                       //基类引用调用虚函数,派生类中的disp版本                
      return 0;
}

输出结果:

hello,base
hello,child

3)类内访问,使用this指针。

4)在构造函数或析构函数中进行访问

class   base
{
    public:
         virtual  void  disp()
         {
              cout<<"hello,base"<<endl;
          }
};

class  child:public  base
{ 
      public:
          child()
         {
              disp();
         }
          void disp()
          {
                cout <<"hello,child"<<endl;
           }
};

在上述代码中,类创建child对象时,输出信息总是“hello,child”。换言之,在child的构造函数中,无论是用disp()还是this->disp()来调用,编译器都将之解释为“child::disp()”,此时,若想在child构造函数中调用base类disp函数,必须使用base::disp()的形式;

2.纯虚函数与抽象类

纯虚函数是一种特殊的虚函数,它是指不必再基类中定义,但必须在派生类中被覆盖的函数。其形式如下:

virtual   类型  函数名   (参数表)=0

一个类可以包含多种纯虚函数。只要类中含有一个纯虚函数,该类便为抽象类。一个抽象类只能作为基类来派生新类,不能创建抽象类的对象。

1)另一种抽象类:类中只定义了protect型的构造函数。

2)private型构造函数。不能在外部函数和派生类中使用“类名+对象吗”创建实例,但可以通过类的static函数成员来创建类的实例。

3)虚析构函数。虽然构造函数不能被声明为虚函数,但析构函数可以,形式如下:

virtual   ~析构函数名();

析构函数调用不当带来的内存泄漏:

class   base
{
    privatechar* data
    public:
         base()
         {
              data  = new char[64];
              cout<<"base类构造函数被调用"<<endl;
          };
         ~base()
         {
               delete [] data;
              cout<<"base类析构函数被调用"<<endl;
          };         
};

class   child : public  base
{
    privatechar* m_data
    public:
         child():base()
         {
              m_data  = new char[64];
              cout<<"child类构造函数被调用"<<endl;
          };
         ~child()
         {
               delete [] m_data;
              cout<<"child类析构函数被调用"<<endl;
          };         
};

int main()
{
      base *pB = new child;
      delete pB;
      return 0;
}

输出结果:

base类析构函数被调用
child类析构函数被调用
base类析构函数被调用

这段程序编译连接没有错,但是在“delete pB”中pB类型决定了调用哪个类的析构函数,因此编译器会调用base类的析构函数,这意味这child类析构函数没有被调用,child类中的m_data指向的内存泄漏了。

解决方案:将基类中的析构函数定义为虚析构函数就能解决这个问题。

原文地址:https://www.cnblogs.com/wlzy/p/5901875.html