【C++】浅谈对多态的理解

多态

多态是C++中面向对象设计里一个重要的内容。所谓多态是指一个实体同时具有多种形式,通俗来讲就是同一个操作对于不同的对象,可以有不同的结果。
举个例子:

class Base
{
public:
    virtual void FunTest()
    {
        cout << "This is a Base class function" << endl;
    }
};
class Derived1: public Base
{
public:
    virtual void FunTest()
    {
        cout << "This is a Derived1 class function" << endl;
    }
};
class Derived2 : public Base
{
public:
    virtual void FunTest()
    {
        cout << "This is a Derived2 class function" << endl;
    }
};
int main()
{
    for (int i = 0; i < 10;i++)
    {
        Base* b;
        int a;
        cin >> a;
        if (a % 2 == 1)
            b = new Derived1;
        else
            b = new Derived2;
        b->FunTest();
    }
    return 0;
}

【C++】浅谈对多态的理解_第1张图片

多态可以分为静态多态和动态多态:

  • 静态多态:发生在编译阶段,具体变现为函数重载,泛型编程。
  • 动态多态:发生在程序运行时,需要满足两个条件。
    -1.虚函数,在派生类中对基类虚函数重写;
    -2.在继承体系中,通过基类类型的指针或引用来调用虚函数。

虚函数:在基类,使用关键字virtual声明,并在派生类中进行重写,用于实现多态性,通过基类的指针或引用调用。
注:1.在派生类中可以不加virtual,但建议加上,代码可读性较强。
  2.如果在类外定义虚函数,只能在函数声明时加virtual关键字,在定义时不需要。
重写:在继承体系中,如果基类中有虚函数,在派生类中有和基类虚函数原型完全相同的函数。
注:两个例外
  1.协变:基类返回基类指针(或引用),派生类返回派生类的指针(或引用);
  2.析构函数:建议最好将析构函数声明为虚函数。
  
构造函数,友元函数,静态函数不能声明为虚函数,赋值运算符重载不建议声明为虚函数,为什么?
  对于构造函数,从内存的角度看,虚函数的调用需要虚表,虚表储存在对象的内存空间,如果构造函数声明为虚函数,那就需要虚表来调用,但是对象没有实例化,没有内存空间,更没有虚表来调用构造函数了;另外,构造函数的调用是创建对象时自动调用的,而虚函数的调用是通过基类的指针去调用,所以构造函数不能声明为虚函数;
  对于友元函数和static修饰的静态函数,友元函数不属于该类的成员函数,不能被继承,虚函数的调用需要对象,也不能声明为虚函数;静态函数可以被继承,但对于静态函数,每个类共享一份代码,它是属于类的,调用不需要对象,所以不能声明为虚函数;
  赋值运算符重载,将赋值运算符重载声明为虚函数,容易造成混淆,因为虚函数要求必须在基类和派生类中函数原型相同,参数也就需要相同,基类的形参是自身类型的引用,派生类的形参也为基类类型的引用,对派生类而言,这个操作符与复制操作并不相同,造成程序赋值运算混乱;
  析构函数,基类的指针指向派生类的对象,如果涉及到动态内存开辟,析构函数如果没有声明为虚函数,那么使用基类指针调用析构函数可能会造成内存泄漏,所以建议将析构函数声明为虚函数。

 函数的重载,继承体系中的同名隐藏,多态中的重写极其相似,但仍有所区别:
1 .函数的重载:在同一个作用域,函数名相同,参数列表不同,与返回值类型无关;
2 .同名隐藏:在继承体系中,如果基类和派生类有相同的名称的成员,如果使用派生类对象调用基类和派生类中的同名成员,优先调用派生类中的成员,同名成员仅名称相同,与函数原型无关,与类型无关;
3 .函数重写(覆盖):在继承体系中,如果基类中有虚函数,在派生类中有和基类虚函数原型完全相同的函数。

你可能感兴趣的:(C++)