C++多态

一.多态概念

在这里插入图片描述
在类这一方面,多态就是:
同一个父类函数由不同的子类对象去调用,产生不同的行为

二.多态定义与实现

学习了多态的概念之后,下面我们来看一下如何构成多态

1.多态的构成条件

多态的构成条件比较严格

1.被调用的函数必须是虚函数,且子类必须对父类的虚函数进行重写

2.必须通过父类的指针或者父类的引用来调用这个虚函数

那么什么是虚函数呢?
什么又是重写呢?
为什么必须要通过父类的指针或者引用来调用这个虚函数呢?
这些我们都会一一说明的
最后一点涉及到多态的原理,等我们介绍完多态的原理之后大家就明白了

2.虚函数的重写

虚函数就是被virtual关键字修饰的类的成员函数
比如:

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

Person当中BuyTicket这个函数就是一个虚函数
那么什么又是重写呢?
在这里插入图片描述
比方说:

class Student : public Person 
{
   
public:
	virtual void BuyTicket()
	{
   
		cout << "学生买票-半价" << endl; 
	}
	//子类的虚函数重写父类的虚函数时,可以不用加virtual关键字
	//因此下面这种写法也是正确的
	//void BuyTicket() { cout << "学生买票-半价" << endl; }
};

学生类这个子类继承了Person类,又实现了一次BuyTicket函数
这就是重写

3.多态函数调用与普通函数调用

1.多态的演示-多态调用

介绍完虚函数重写之后
下面我们来演示一下多态调用

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

class Student : public Person 
{
   
public:
	virtual void BuyTicket()
	{
   
		cout << "学生买票-半价" << endl; 
	}
	//子类重写父类的虚函数时,可以不用加virtual关键字
	//因此下面这种写法也是正确的
	//void BuyTicket() { cout << "学生买票-半价" << endl; }
};

class Soldier : public Person
{
   
public:
	virtual void BuyTicket()
	{
   
		cout << "军人买票-优先" << endl;
	}
	//void BuyTicket() { cout << "军人买票-优先" << endl; } yes
};

//这里的参数必须是父类的指针或者引用
void Func(Person& p)
{
   
	p.BuyTicket();
}

int main()
{
   
	Person ps;
	Student st;
	Soldier sr;
	Func(ps);
	Func(st);
	Func(sr);
	return 0;
}

在这里插入图片描述
同理父类的指针调用也属于多态调用
在这里插入图片描述
这种调用称为多态调用
多态调用的特点是:
对于传入的父类指针或者引用
1.如果 指向父类则会调用父类的虚函数
2.如果 指向子类则会调用子类的虚函数

2.普通调用

对于一个函数调用来说如果不满足多态调用,那么就是普通的函数调用
下面我们传入父类对象来看看:
是不是真的只有当使用父类的引用或者指针进行函数调用时才会发生多态调用?
在这里插入图片描述
此时我们发现:只传入父类对象的话,不会构成多态,
因此这三次调用都是调用的父类的虚函数
属于普通调用

3.为什么父类对象不能实现多态呢?

这一点我们介绍多态的原理的时候会详细说明的

4.虚函数重写的注意事项

注意:

1.子类的虚函数重写父类的虚函数时,可以不用加virtual关键字

因此下面这种写法也是正确的
void BuyTicket() {
    cout << "学生买票-半价" << endl; }

因为继承之后父类的虚函数依旧在子类中保持虚函数的属性
因此子类的虚函数重写父类虚函数时可以不用加virtual关键字

不过为了代码的可读性,不建议这样使用,最好还是加上virtual

2.协变:父类与子类虚函数的返回值类型可以不同

但是要求返回值必须是父子类关系的指针或者引用

例如:
在这里插入图片描述
关于协变大家了解即可,并不常见

5.析构函数的重写

我们之前在介绍继承的时候曾经提到过:
编译器会对析构函数名进行特殊处理,处理成destructor()
当时说这是因为多态的原因
今天我们就来解释一下为什么要这样处理呢?

如果析构函数不加virtual的话
对于下面这种情况,就会发生内存泄漏:

class Person
{
   
public:
	~Person()
	{
   
		cout << "Person类的析构函数调用" << endl;
		delete[] _p;
		_p = nullptr;
	}
protected:
	int* _p = new int[20];
};

class Student : public Person
{
   
public:
	~Student()
	{
   
		cout << "Student类的析构函数调用" << endl;
		delete[] _s;
		_s = nullptr;
	}
protected:
	int* _s = new int[30];
};
评论 78
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

program-learner

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

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

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

打赏作者

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

抵扣说明:

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

余额充值