C++进阶之路---多态(一)

46f376db7bb34b068d1fb00b70fa40b7.jpeg

顾得泉:个人主页

个人专栏:《Linux操作系统》 《C++从入门到精通》  《LeedCode刷题》

键盘敲烂,年薪百万!


一、多态的概念

1.概念

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

feedeaf1ad534d4e9c6b3a5782963dc2.png

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

       再举个栗子: 最近为了争夺在线支付市场,支付宝年底经常会做诱人的扫红包-支付-给奖励金的活动。那么大家想想为什么有人扫的红包又大又新鲜8块、10块…,而有人扫的红包都是1毛,5毛…。其实这背后也是一个多态行为。支付宝首先会分析你的账户数据,比如你是新用户、比如你没有经常支付宝支付等等,那么你需要被鼓励使用支付宝,那么就你扫码金额 =random()%99;比如你经常使用支付宝支付或者支付宝账户中常年没钱,那么就不需要太鼓励你去使用支付宝,那么就你扫码金额 = random()%1;总结一下:同样是扫码动作,不同的用户扫得到的不一样的红包,这也是一种多态行为。

       ps:支付宝红包问题纯属瞎编,大家仅供娱乐。


二、多态的定义及实现

1.多态的构成条件

       多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价。

那么在继承中要构成多态还有两个条件:

1.必须通过基类(父类)的指针或者引用调用虚函数
2.被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写(重写条件:函数名/参数/返回值相同 协变例外)

b1c15fe3bdd949c69f1fae16e0b64b62.png

2.虚函数

虚函数:即被virtual修饰的类成员函数称为虚函数。

class Person 
{
public:
     virtual void BuyTicket() { cout << "买票-全价" << endl;}
};

3.虚函数的重写

class Person
{
public:
    virtual void Buyticket()
    {
        cout << "票价--全价" << endl;
    }
};

class Student : public Person
{
public:
    virtual void Buyticket()
    {
        cout << "票价--半价" << endl;
    }
    /*void BuyTicket() { cout << "买票-半价" << endl; }*/
};

       虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。
       注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样使用。

void func(Person& p)
{
    p.Buyticket();
}
int main()
{
    Person p;
    Student s;

    func(p);
    func(s);

    return 0;
}

虚函数重写的两个例外:

1.协变(基类与派生类虚函数返回值类型不同)

       派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。(了解)

class A{};
class B : public A {};
class Person 
{
public:
     virtual A* f() {return new A;}
};
class Student : public Person 
{
public:
     virtual B* f() {return new B;}
};

2.析构函数的重写(基类与派生类析构函数的名字不同)

       如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。

class Person 
{
public:
     virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person 
{
public:
     virtual ~Student() { cout << "~Student()" << endl; }
};
/*只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函
数,才能构成多态,才能保证p1和p2指向的对象正确的调用析构函数。*/
int main()
{
     Person* p1 = new Person;
     Person* p2 = new Student;
     delete p1;
     delete p2;
     return 0;
}

4.C++11 override 和 final

       从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失,因此:C++11提供了override和final两个关键字,可以帮助用户检测是否重写。

        1.final:修饰虚函数,表示该虚函数不能再被重写

class Car
{
public:
     virtual void Drive() final {}
};
class Benz :public Car
{
public:
     virtual void Drive() {cout << "Benz-舒适" << endl;}
};

        2.override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

class Car
{
public:
     virtual void Drive(){}
};
class Benz :public Car 
{
public:
     virtual void Drive() override {cout << "Benz-舒适" << endl;}
};

5.重载、覆盖、隐藏)的对比

3ac61f7477484b54999d000a1b39f35f.png


三、抽象类

1.概念

       在虚函数的后面写上 =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();
}

2.接口继承和实现继承

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


结语:C++关于多态的分享到这里就结束了,希望本篇文章的分享会对大家的学习带来些许帮助,如果大家有什么问题,欢迎大家在评论区留言~~~ 

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-03-14 11:58:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-14 11:58:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-14 11:58:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-14 11:58:03       18 阅读

热门阅读

  1. 临近取样(KNN)算法基本原理&sklearn实现

    2024-03-14 11:58:03       16 阅读
  2. 各个类型和Json类型的相互转换

    2024-03-14 11:58:03       22 阅读
  3. 【算法】KY33 密码翻译

    2024-03-14 11:58:03       18 阅读
  4. 力扣Python方法解析

    2024-03-14 11:58:03       19 阅读
  5. Element Plus与Ant Design Vue:选型对比

    2024-03-14 11:58:03       18 阅读
  6. JVM-2

    JVM-2

    2024-03-14 11:58:03      16 阅读
  7. Ribbon

    2024-03-14 11:58:03       20 阅读
  8. SpringBoot中的HttpServletRequest

    2024-03-14 11:58:03       19 阅读
  9. Linux tar静态编译过程记录

    2024-03-14 11:58:03       21 阅读
  10. Vivado原语模板

    2024-03-14 11:58:03       19 阅读
  11. 学习Android的第二十七天

    2024-03-14 11:58:03       17 阅读