C++ 多态的定义及实现

C++ 多态的定义及实现

1. 多态定义的构成条件

多态是在不同继承关系的类对象,去调同一函数,产生了不同的行为。

就是说,有一对继承关系的两个类,这两个类里面都有一个函数且名字、参数、返回值均相同,然后我们通过调用函数来实现不同类对象完成不同的事件。

但是构成多态还有两个条件:

  1. 调用函数的对象必须是指针或者引用。
  2. 被调用的函数必须是虚函数,且完成了虚函数的重写。

说了这么多,怎么实现呢?我们先来看一段代码。

#include <iostream>

class Person 
{
public:
    virtual void BuyTicket(int)
    {   
        std::cout << "Adult need Full Fare!" << std::endl;
    }   
};

class Child : public Person
{
public:
    virtual void BuyTicket(int)
    {   
        std::cout << "Child Free!" << std::endl;
    }   
};

void fun(Person& obj)
{
    obj.BuyTicket(1);                                                                                                                             
}

int main(void)
{
    Person p;
    Child c;

    fun(p);
    fun(c);

    return 0;
}

在这里插入图片描述

  1. 调用函数就是这里的fun,参数int没有实际意义,就是为了体现函数重写必须要返回值一样、函数名一样和参数一样。
  2. 被调用的函数必须是虚函数,也就是说必须要在两个产生多态的函数前面加virtual关键字
  3. 调用函数的形参对象必须是基类对象,这里是因为派生类只能给基类赋值,会发生切片操作。基类不能给派生类赋值。
  4. 调用函数的参数必须是指针或引用,因为派生类改变了虚表,那么这个虚表就属于派生类对象,赋值的时候只会把基类的成员给过去,虚表指针不会给。所以在调用函数的时候会发生语法检查,如果满足多态的条件,就会触发寻找虚表中虚函数地址。如果不满足条件,则会直接用基类对象调用基类函数。

上面牵扯出两个概念:

  1. 虚函数:虚函数就是在类的成员函数前面加virtual关键字。

  2. 虚函数重写:虚函数的重写:派生类中有一个跟基类的完全相同虚函数,我们就称子类的虚函数重写了基类的虚函数。

    完全相同是指:函数名、参数、返回值都相同。另外虚函数的重写也叫作虚函数的覆盖

    虚函数重写有一个例外错误:协变

    重写的虚函数的返回值可以不同,但是必须分别是基类指针或引用和派生类指针或引用。

    这种情况在VS会报错,但是在linuxG++下不会

    #include <iostream>
      
    class A                                                                  
    { 
    public:                                                                  
        virtual A* fun()
        {
            return new A;                                                    
        }
    };
    
    class B : public A
    {
    public:
        virtual B* fun()
        {
            return new B;
        }
    };
    

不规范重写行为:

就是在派生类的重写函数加了virtual关键字,但是在派生类的重写函数前不加。

这样不会报错,因为继承的原因,将这个virtual的性质继承了下来,但是这样写不规范,

如果两个函数构成重写,那么要在两个函数前都加上virtual关键字。

2.析构函数重写问题

基类中的析构函数如果是虚函数,那么派生类的析构函数就重写了基类的析构函数。这里他们的函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor,这也说明的基类的析构函数最好写成虚函数。

将析构函数定义为虚函数的原因:

因为基类指针可能指向派生类,当delete的时候,如果不定为虚函数,系统会直接调用基类的析构函数,这个时候派生类就有一部分没有被释放,就会造成可怕的内存泄漏问题。

若定义为虚函数构成多态,那么就会先调用派生类的析构函数然后派生类的析构函数会自动调用基类的析构函数,这个结果满足我们的本意。

所以!在继承的时候,尽量把基类的析构函数定义为虚函数,这样继承下去的派生类的析构函数也会被变成虚函数构成多态。

3.抽象类

在虚函数的后面写上 =0 ,则这个函数就变成纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。

这个纯虚函数的作用就是强迫我们重写虚函数,构成多态。这样更加体现出了接口继承。

#include <iostream>

class Person
{
public:
    virtual void Strength() = 0;
};

class Adult : public Person
{
public:
    virtual void Strength()
    {   
        std::cout << "Adult have big Strength!" << std::endl;
    }   
};

class Child : public Person
{
public:
    virtual void Strength()
    {   
        std::cout << "Child have Small Strength!" << std::endl;                                                                                   
    }   
};
4.C++11 override 和 final

override:

override是用来检查函数是否重写,是在virtual void fun() override {}这里加上,然后来检查的。实际中,建议这样写。

final:

final是在class A final {};这里加上,目的是为了不让这个类被继承。
或者,在一个函数后加,表示这个函数不能被重写。void fun() final {}

  • 89
    点赞
  • 312
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值