C++57个入门知识点_32 初识多态及虚函数-核心(加virtual的函数称为虚函数;某一个函数在父子类有不同实现,运行时对象自行决定调用哪一类实现;没virtual调父类同名函数,否则调子类函数)

前面我们学习了继承的相关知识,本篇开始学习C++中最核心的知识点-多态。本篇将会演示多态所能实现的功能及基本概念。

总结:

  1. 利用父类指针数组实现不同国家说不同语言(加一个类型变量int m_nType;来判断调用哪个类)
  2. 利用虚函数和多态实现上面功能
  • C++中提供了virtual关键字,加了virtual的函数称为虚函数
  • 使用虚函数实现的效果就称为多态某一个函数在父类子类有不同的实现,运行时对象自行决定调用哪一个类下实现
  • virtual调父类同名函数,有virtual调子类同名函数

在《C++编程自学宝典》中对应部分总结的比较好的一句话:

  • 通过基类指针调用派生类方法也称为方法调度。可以参与方法调度的方法在基类中是使用关键字 virtual 标记的,因此通常也被称为虚方法。
  • 当使用从基类派生的虚方法时,派生类也将拥有一个 vptr,但是编译器将让它指向派生类的 vtable,即编译器将使用派生类的虚方法实现的地址填充 vtable。(20240310:虚表就是一个函数指针列表)

学习数据结构时黑马提到的一种说法:多态是父用后来写的代码,继承是后来人用父写的代码

1. 利用对象实现不同国家人说不同的语言

按照函数隐藏可知,以下代码中子类对象调用本身的函数。

#include <iostream>

//面向对象:多态
class CPerson
{
public:
	CPerson() {

	}

    void speak() {
		//printf("说人话\r\n");
	}
};

class CChinese :public CPerson {
public:
	CChinese() {

	}
	
	void speak() {
		printf("说中文\r\n");
	}
};

class CEnglish :public CPerson {
public:
	CEnglish() {
	}
	
	void speak() {
		printf("说英语\r\n");
	}
};

int main(int argc, char* argv[])
{
	CChinese chs;
	chs.speak();

	CEnglish eng;
	eng.speak();
	
	return 0;
}

运行结果:运行结果中国人说中文,英国人说英文
在这里插入图片描述

2. 父类指针数组实现不同国家人说不同语言

一种场景下,在一个聊天室中来了很多人,当我们需要模拟一群人说话时,代码应该如何去写?

一群人需要使用某种数据结构去表达,使用父类数组结构表达有三个人:CPerson ary[3],但是CPerson ary[3]不能指明是哪国人,应该用指针表示出这三个人,就变为:CPerson* ary[3]
以下代码中:ary[0] = &chs;将子类指针转换为父类指针,本质上还是子类,这是安全的。

可参考C++57个入门知识点_29 从内存角度看继承(子类继承时包含(复制)了父类所有成员变量,但由于继承的可见性,只可对部分进行访问;运行时可修改父类成员;子类转父类安全,父类转子类不安全(越界访问))

  • 使用子类强转父类指针方式实现调用的函数仍为父类的函数
int main(int argc,char* argv[])
{
	CChinese chs;
	chs.speak();
	CChinese chs2;

	CEnglish eng;
	eng.speak();

	CPerson* ary[3]; //用父类指针指向子类

	//把子类指针强转为父类,父类的三个指针调用的都是父类的
	ary[0] = &chs;
	ary[1] = &eng;
	ary[2] = &chs2;

	return 0;
}

模拟每个人说话

#include <iostream>

//面向对象:多态

class CPerson
{
public:
	CPerson() {

	}

    void speak() {
		//printf("说人话\r\n");
	}

};

class CChinese :public CPerson {
public:
	CChinese() {

	}
	void speak() {
		printf("说中文\r\n");
	}
};

class CEnglish :public CPerson {
public:
	CEnglish() {

	}

	void speak() {
		printf("说英语\r\n");
	}
};

int main(int argc, char* argv[])
{
	CChinese chs;
	CChinese chs2;

	CEnglish eng;

	CPerson* ary[3]; //用父类指针指向子类

//把子类指针强转为父类,父类的三个指针调用的都是父类的
	ary[0] = &chs;
	ary[1] = &eng;
	ary[2] = &chs2;
	
	//模拟每个人说话
	for (int i = 0; i < 3; i++)
	{
		printf("%d:", i + 1);
		ary[i]->speak();
	}
	return 0;
}

运行结果:跟预期中三个人分别说本国话的结果不一致
在这里插入图片描述
为什么会出现上述结果呢?这是因为ary[0] = &chs;将子类的指针强转为父类,父类的3个指针直接调用是当前自己的“说人话”

  • 增加类型判断变量实现不同国家说不同语言

显然上面的结果不符合我们的想法,我们想要各国人说自己的语言,怎么去做呢?
我们需要对ary[i]对象指针进行判断,如果是中国人就说中国话,应该人就说英文,增加一个类型变量int m_nType;来判断调用哪个类。

因为 子类强转为父类其内部的成员变量依旧是子类的值,因此可以用来判断调用哪个类的成员函数
在这里插入图片描述
整体代码如下:

#include <iostream>

//面向对象:多态

class CPerson
{
public:
	CPerson() {
		m_nType = 0;//普通的人
	}

    void speak() {
		printf("说人话\r\n");
	}

	int m_nType;

};

class CChinese :public CPerson {
public:
	CChinese() {
		m_nType = 1;//中国人
	}
	void speak() {
		printf("说中文\r\n");
	}
};

class CEnglish :public CPerson {
public:
	CEnglish() {
		m_nType = 2;//英国人 
	}

	void speak() {
		printf("说英语\r\n");
	}
};

int main(int argc, char* argv[])
{
	CChinese chs;
	//chs.speak();

	CChinese chs2;

	CEnglish eng;
	//eng.speak();

	CPerson* ary[3]; //用父类指针指向子类

//把子类指针强转为父类,父类的三个指针调用的都是父类的
	ary[0] = &chs;
	ary[1] = &eng;
	ary[2] = &chs2;
	
	//模拟每个人说话
	for (int i = 0; i < 3; i++)
	{
		printf("%d:", i + 1);

		if (ary[i]->m_nType == 1) //利用m_nType判断调用哪个类的成员函数
		{
			//表示该对象是中国人
			//父类转子类后再子类转父类是可以强制转换的
			CChinese* pChs = (CChinese*)ary[i];
			pChs->speak();
		}
		else if (ary[i]->m_nType == 2)
		{
			//表示该对象是英国人
			//父类转子类后再子类转父类是可以强制转换的
			CEnglish* pEng = (CEnglish*)ary[i];
			pEng->speak();
		}
	}
	return 0;
}

运行结果如下:
在这里插入图片描述
上面的代码实现了我们要求,但是整体十分冗余,需要自己写代码进行判断。

3. 虚函数和多态

  • C++中提供了virtual关键字,加了virtual的函数称为虚函数
  • 使用虚函数实现的效果就称为多态某一个函数在父类子类有不同的实现,运行时对象自行决定调用哪一个类下实现
  • virtual调父类同名函数,有virtual调子类同名函数

CPerson类的函数成员中加virtual

#include <iostream>

//面向对象:多态

class CPerson
{
public:
	CPerson() {
		m_nType = 0;//普通的人
	}

    virtual void speak() {
		printf("说人话\r\n");
	}

	int m_nType;
};

class CChinese :public CPerson {
public:
	CChinese() {
		m_nType = 1;//中国人
	}
	void speak() {
		printf("说中文\r\n");
	}
};

class CEnglish :public CPerson {
public:
	CEnglish() {
		m_nType = 2;//英国人 
	}

	void speak() {
		printf("说英语\r\n");
	}
};

int main(int argc, char* argv[])
{
	CChinese chs;
	//chs.speak();

	CChinese chs2;

	CEnglish eng;
	//eng.speak();

	CPerson* ary[3]; //用父类指针指向子类

//把子类指针强转为父类,父类的三个指针调用的都是父类的
	ary[0] = &chs;
	ary[1] = &eng;
	ary[2] = &chs2;
	
	//模拟每个人说话
	for (int i = 0; i < 3; i++)
	{
		printf("%d:", i + 1);
		ary[i]->speak();
	}

	return 0;
}

运行结果:运行结果与上面的代码一致
在这里插入图片描述

4.学习视频地址:C++57个入门知识点_32 初识多态及虚函数-核心

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十月旧城

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值