前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++多态特性

C++多态特性

作者头像
初阶牛
发布2023-11-12 08:34:16
1340
发布2023-11-12 08:34:16
举报
文章被收录于专栏:C语言基础

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

前言

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

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

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

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

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

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

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

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

一、多态的实现

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

代码语言:javascript
复制
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);	//1
    test1(s1);	//2
    test1(t1);	//3
    return 0;
}

运行结果:

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

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

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

在这里插入图片描述
在这里插入图片描述

运行结果:

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

🍭实现多态的条件:

  1. 继承关系:多态需要通过继承关系来实现,子类必须要继承父类。
  2. 方法重写(虚函数实现):子类必须重写父类的方法,这样才能表现出不同的行为。
  3. 父类指针/引用指向子类对象:必须是父类的指针或者引用调用虚函数,才能进行多态操作。

二、何为虚函数?

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

普通函数:

代码语言:javascript
复制
	virtual void Have_lunch()
	{
		cout << "你需要支付8元的午餐费!" << endl;
	}

虚函数:

代码语言:javascript
复制
	virtual void Have_lunch()
	{
		cout << "你需要支付8元的午餐费!" << endl;
	}

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

  1. 返回类型要相同:
在这里插入图片描述
在这里插入图片描述
  1. 参数类型要相同:
在这里插入图片描述
在这里插入图片描述
  1. 函数名相同:这个就不演示了,肯定无法实现多态.

🍉就你小子特殊?

虚函数的特殊情况: 斜变

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

代码语言:javascript
复制
//斜变
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. 为什么析构函数要实现多态?

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

示例:

代码语言:javascript
复制
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: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

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

代码语言:javascript
复制
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:修饰虚函数,表示该虚函数不能再被重写.

代码语言:javascript
复制
class People
{
public:
	 virtual void Have_lunch()final
	{
		cout << "你需要支付10元的午餐费!" << endl;
	}
};

class Student :public People
{
public:
	virtual void Have_lunch()
	{
		cout << "你需要支付8元的午餐费!" << endl;
	}
};
在这里插入图片描述
在这里插入图片描述

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、多态的实现
    • 🍭实现多态的条件:
    • 二、何为虚函数?
      • 🍉就你小子特殊?
        • 2.1 析构函数可以是虚函数吗?
        • 三、 关键字override 和 final
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档