C++多态特性

编程入门 行业动态 更新时间:2024-10-26 01:26:36

C++多态<a href=https://www.elefans.com/category/jswz/34/1769892.html style=特性"/>

C++多态特性

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻强烈推荐优质专栏: 🍔🍟🌯C++的世界(持续更新中)
🐻推荐专栏1: 🍔🍟🌯C语言初阶
🐻推荐专栏2: 🍔🍟🌯C语言进阶
🔑个人信条: 🌵知行合一
🍉本篇简介:>:讲解C++中多态知识,不同对象不同的处理方式.
金句分享:
✨不要总是抱怨环境,鼓起勇气战胜它!✨

前言

多态的概念:就是多种形态,当不同的对象去完成某个动作时会产生出不同的状态。

比如:在现实生活中,我们经常遇到不同身份的人享受的待遇不一样.

购买回家的火车票时,如果是购买的学生票,会有打折优惠.
在参加某宝,某团的活动时,新用户获得的优惠券就会比活跃用户的额度高不少.

C++中的多态(polymorphism)指的是同一个函数名在不同对象上可以有不同的行为。
它可以通过两种方式实现:
(1)虚函数(virtual function

虚函数是在基类中声明的函数,它可以被派生类重写,实现多态的效果。在派生类中实现的函数可以覆盖基类中的同名函数,而且会在运行时的对象类型上调用合适的函数。通过将基类指针或引用指向派生类对象,可以实现动态多态性。

(2)模板(template)。(前面已经讲过了)

模板是一种通用的代码库,可以为不同的类型提供相同的代码实现。使用模板可以实现静态多态性。在编译期间,编译器会依据模板中使用的类型,生成适当的代码。

使用多态可以让代码更加灵活,易于维护和扩展。此外,多态也是对象导向程序设计中的核心概念之一。

目录

  • 前言
  • 一、多态的实现
    • 🍭实现多态的条件:
  • 二、何为虚函数?
    • 🍉就你小子特殊?
    • 2.1 析构函数可以是虚函数吗?
  • 三、 关键字override 和 final

一、多态的实现

代码1,2,3的执行结果是什么?

class People
{
public:void Have_lunch(){cout << "你需要支付10元的午餐费!" << endl;}
};class Student:public People
{
public:void Have_lunch(){cout << "你需要支付8元的午餐费!" << endl;}
};class Teacher:public People
{
public:void Have_lunch(){cout << "老师,您好,欢迎就餐!已扣除5元餐费." << endl;}
};void test1(People& p1)
{p1.Have_lunch();
}int main()
{People p1;Student s1;Teacher t1;test1(p1);	//1test1(s1);	//2test1(t1);	//3return 0;
}


运行结果:

你需要支付10元的午餐费!
你需要支付10元的午餐费!
你需要支付10元的午餐费!

在前面的继承章节,我们知道,基类和派生类是两个不同的作用域,定义同名的两个函数时,会形成隐藏操作.所以这里的打印结果都是一样的.

如果想实现态,也就是不同的对象调用同一个函数,实现不同的结果,以下代码就是实现的例子,试着观察一下区别吧.

运行结果:

你需要支付10元的午餐费!
你需要支付8元的午餐费!
老师,您好,欢迎就餐!已扣除5元餐费.

🍭实现多态的条件:

  1. 继承关系:多态需要通过继承关系来实现,子类必须要继承父类。

  2. 方法重写(虚函数实现):子类必须重写父类的方法,这样才能表现出不同的行为。

  3. 父类指针/引用指向子类对象:必须是父类的指针或者引用调用虚函数,才能进行多态操作。

二、何为虚函数?

C++中的虚函数是一种特殊的成员函数,用于在继承关系中实现多态性。在父类中通过关键字virtual声明的函数为虚函数,子类可以覆盖并重新实现(重写)该函数。当通过父类的指针或引用调用虚函数时,实际调用的是子类中的实现,而不是父类的实现。这样就实现了多态.

普通函数:

	virtual void Have_lunch(){cout << "你需要支付8元的午餐费!" << endl;}

虚函数:

	virtual void Have_lunch(){cout << "你需要支付8元的午餐费!" << endl;}

虚函数的重写要求十分严格:

  1. 返回类型要相同:
  2. 参数类型要相同:
  3. 函数名相同:这个就不演示了,肯定无法实现多态.

🍉就你小子特殊?

虚函数的特殊情况: 斜变

派生类重写基类虚函数时,与基类虚函数返回值类型不同。
基类虚函数返回基类对象的指针或者引用.
派生类虚函数返回派生类对象的指针或者引用时.
这种情况,即使返回值不同,此时也满足虚函数的重写,也可以实现多态.

//斜变
class People
{
public:virtual People* Have_lunch()		//返回基类的指针或者引用{cout << "你需要支付10元的午餐费!" << endl;return nullptr;}
};class Student :public People
{
public:virtual Student* Have_lunch()		//返回派生类的指针或者引用{cout << "你需要支付8元的午餐费!" << endl;return nullptr;}
};class Teacher :public People
{
public:virtual Teacher* Have_lunch()		/返回派生类的指针或者引用{cout << "老师,您好,欢迎就餐!已扣除5元餐费." << endl;return nullptr;}
};

运行结果:

2.1 析构函数可以是虚函数吗?

显然,基类与派生类析构函数的名字不同 .

看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。即所有对象的析构函数,在编译后,都被处理为统一的名字:destructor.

  1. 为什么析构函数名要被统一转化为destructor?

因为要实现多态需要函数名相同.

  1. 为什么析构函数要实现多态?

因为析构函数实现多态了以后,才能实现在析构基类和派生类时,各自调用自己的析构函数,防止内存泄漏!

示例:

class People
{
public:~People(){free(arr);cout << "~People()" << endl;}
private:int* arr = new int[10];		
};class Student :public People
{
public:~Student(){free(arr2);cout << "~Student()" << endl;}
private:int* arr2 = new int[10];
};int main()
{People* p1 = new People;		People* p2 = new Student;		//基类指针指向派生类delete p1;delete p2;		//arr2未释放return 0;
}

运行结果:


显然,在未实现多态的情况下,当基类指针指向派生类时,调用析构函数都只能调用基类的析构函数.这就导致了派生类存在成员变量并没有释放空间,也就导致了内存泄漏!


综上,析构函数可以是虚函数,而且还强烈建议将析构函数写成虚函数,实现多态.

虚函数重写需要遵守以下条件:

  1. 函数名称、参数列表和返回类型在父类和子类中必须完全相同。(三同)
  2. 函数在父类中必须被声明为virtual关键字,否则在子类中重写将不会产生多态效果。
  3. 函数在子类中必须使用virtual关键字进行声明,以便在运行时确定需要调用的是哪个版本的函数。

三、 关键字override 和 final

先声明一下:
这两个关键字是c++11才出现的,老版本不支持哦!

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

很多时候,我们需要实现多态,但是由于一时疏忽,将函数名写错了一个字母,或者返回值,参数列表等不同,而导致无法进行虚函数的重写.

class People
{
public:virtual void Have_lunch(){cout << "你需要支付10元的午餐费!" << endl;}
};class Student :public People
{
public:virtual void Have_Lunch()override{cout << "你需要支付8元的午餐费!" << endl;}
};class Teacher :public People
{
public:void Have_lunch()override{cout << "老师,您好,欢迎就餐!已扣除5元餐费." << endl;}
};

有的时候,我们并不像函数成为虚函数,又担心自己不小心满足了虚函数的重写的条件.(其实,博主感觉这种情况很少,也可能是博主目前接触的代码还比较少,没体会到.😂)

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

class People
{
public:virtual void Have_lunch()final{cout << "你需要支付10元的午餐费!" << endl;}
};class Student :public People
{
public:virtual void Have_lunch(){cout << "你需要支付8元的午餐费!" << endl;}
};

对于多态,本文就介绍到这里了,多态的实现原理是什么?菱形继承搭配虚拟继承会不会很头痛?
想要更加深层的理解C++的多态,下一篇文章,咱们不见不散!

更多推荐

C++多态特性

本文发布于:2023-11-15 10:17:04,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1598036.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:特性   多态

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!