【C++】多态

目录

      • 1. 多态的概念
      • 2. 多态的定义及实现
        • 2.1 虚函数
        • 2.2 多态构成的条件
          • 2.2.1 虚函数重写
          • 2.2.2 基类指针或者引用调用虚函数
      • 3. 虚函数重写的两个例外
        • 3.1 协变
        • 3.2 析构函数重写
      • 4. C++11 override 和 final
        • 4.1 final
        • 4.2 override
      • 5. 重载、覆盖(重写)、隐藏(重定义)的对比
        • 6. 抽象类

1. 多态的概念

通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会 产生出不同的状态。

举个简单的例子:

比如买票这个行为,当普通人买票时,是全价买票;学生买票时,是半价买票;军人买票时是优先买票。

2. 多态的定义及实现

2.1 虚函数

虚函数:即被virtual修饰的类成员函数称为虚函数
【C++】多态_第1张图片

2.2 多态构成的条件

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。
在继承中要构成多态还有两个条件:

  1. 必须通过基类的指针或者引用调用虚函数
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
2.2.1 虚函数重写

虚函数的重写(覆盖): 派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的 返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。
【C++】多态_第2张图片

注意: 在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样使用。

2.2.2 基类指针或者引用调用虚函数

【C++】多态_第3张图片

这里已经实现了多态,Person类对象ps调用Func函数最终调用Person类的BuyTicket函数,而Student类对象st调用Func最终调用Student类的BuyTicket函数。
最终产生的是不同的结果。

3. 虚函数重写的两个例外

3.1 协变

派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变.
这里返回的基类派生类的指针或引用也可以是其它继承体系中的基类和派生类。
【C++】多态_第4张图片

3.2 析构函数重写

我们来举一个例子:

class Person
{
public:
    ~Person() 
    { 
        cout << "~Person()" << endl; 
    }
};

class Student : public Person
{
public:
    ~Student() 
    { 
        cout << "~Student()" << endl; 
    }
};

int main()
{
    Person* p = new Person;
    Person* s = new Student;
    delete p;
    delete s;

    return 0;
}

我们来看一下上述代码的运行结果:
【C++】多态_第5张图片
这里s指向的是Student子类对象,但是析构的时候只调了父类的析构函数。这里如果子类自己的成员存在资源管理,只调用父类析构,就只清理了子类里父类的部分,那就会发生内存泄漏。

这里会发生这种情况的原因是:这里第一步执行析构是它这个指针是什么类型,它就会执行那个类的析构,所以这里delete s只会执行父类的析构。

所以我们期望**指向的是父类对象,就按父类的析构去走;指向的是子类的对象,就按子类的析构去走。**通过多态我们就可以实现。
【C++】多态_第6张图片
【C++】多态_第7张图片

重写不是要求基类和派生类的虚函数名字一样吗?
注意:这里编译器会对析构函数名进行特殊处理,都会被处理成destrutor()

4. C++11 override 和 final

4.1 final

final 修饰虚函数,该虚函数不能被重写
【C++】多态_第8张图片

4.2 override

override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。
【C++】多态_第9张图片

5. 重载、覆盖(重写)、隐藏(重定义)的对比

【C++】多态_第10张图片

6. 抽象类

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。**包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。**派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

class Car
{
public:
    virtual void Drive() = 0;
};
class Benz : public Car
{
public:
    virtual void Drive()
    {
        cout << "Benz-舒适" << endl;
    }
};
class BMW : public Car
{
public:
    virtual void Drive()
    {
        cout << "BMW-操控" << endl;
    }
};
void Test()
{

    Car *pBenz = new Benz;
    pBenz->Drive();
    Car *pBMW = new BMW;
    pBMW->Drive();
}

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。

你可能感兴趣的:(C++,c++,java,开发语言,学习,数据结构)