前面我们学习了继承的相关知识,本篇开始学习C++中最核心的知识点-多态
。本篇将会演示多态所能实现的功能及基本概念。
总结:
- 利用父类指针数组实现不同国家说不同语言(加一个类型变量
int m_nType;
来判断调用哪个类) - 利用虚函数和多态实现上面功能
- 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] = ŋ
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] = ŋ
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] = ŋ
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] = ŋ
ary[2] = &chs2;
//模拟每个人说话
for (int i = 0; i < 3; i++)
{
printf("%d:", i + 1);
ary[i]->speak();
}
return 0;
}
运行结果:运行结果与上面的代码一致
4.学习视频地址:C++57个入门知识点_32 初识多态及虚函数-核心