C++中多态的使用和建立条件

一、多态引入

多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

根据代码,引入多态的概念:

#include <iostream>
using namespace std;

//定义一个成人类
class Person {
public:
	virtual void BuyTicket() { cout << "成人买票-全价" << endl; }
};

//定义一个学生类
class Student : public Person {
public:
	virtual void BuyTicket() { cout << "学生买票-半价" << endl; }
};

void Func(Person& p)
{
	p.BuyTicket();
}
int main()
{
	Person ps;  //成人对象
	Student st; //学生
	Func(ps);  // void Func(Person& p) 参数传参,函数调用
	Func(st);  // void Func(Person& p) 参数传参,函数调用
	return 0;
}

在这里插入图片描述
执行结果可以看出根据对象的不同,调用出来不同的结果

多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会
产生出不同的状态 。

修改上面的代码,观察结果:
#include <iostream>
using namespace std;

class Person {
public:
	//修改前:virtual void BuyTicket() { cout << "成人买票-全价" << endl; }
	 void BuyTicket() { cout << "成人买票-全价" << endl; }
};

class Student : public Person {
public:
	//修改前:virtual void BuyTicket() { cout << "学生买票-半价" << endl; }
	 void BuyTicket() { cout << "学生买票-半价" << endl; }
};

void Func(Person& p)
{
	p.BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(ps);
	Func(st);
	return 0;
}

在这里插入图片描述
我们去掉virtual 关键字之后,看到结果都是调用父类的BuyTicket()函数。 原因分析:student类继承person类,两个类里面的BuyTicket()函数函数名一样,构成重定义(1.函数名相同 2.两个函数分别在父子类的作用域里 3. 父子类的同名函数不构成重写),所以子类对象赋值给父类对象的时候,会将子类对象中的父类切出来,自然而然调用的是父类的成员函数

我们再修改代码,思考结果:
#include <iostream>
using namespace std;

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

class Student : public Person {
public:
	virtual void BuyTicket() { cout << "学生买票-半价" << endl; }
};

void Func(Person p)  //修改前:Person& p
{
	p.BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(ps);
	Func(st);
	return 0;
}

在这里插入图片描述
这个结果的产生原因和上面的原因是一致的,父类赋值给父类对象自然调用父类的成员函数、子类对象赋值给父类对象造成“切片”,将子类中的父类那一部分切出去,让父类对象调用

再修改代码,看结果:
#include <iostream>
using namespace std;

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

class Student : public Person {
public:
	virtual void BuyTicket() { cout << "学生买票-半价" << endl; }
};

void Func(Person* p)  //修改前:Person& p
{
	p->BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(&ps);  //修改前 Func(ps);
	Func(&st);  //修改前 Func(st);
	return 0;
}

在这里插入图片描述

使用指针的时候又根据指向对象的不同,调用出不同的结果

根据上面的代码结果,我们可以大概感知到多态就是根据不同继承关系的类对象,调用同一函数时产生了不同的行为。那么产生多态的条件是什么呢?



二、多态构成的条件

重写的概念

  • 两个函数分别在子类和父类的作用域中
  • 函数名 / 返回值 / 参数都必须相同
  • 两个函数必须是虚函数 (被virtual 修饰的类成员函数成为虚函数
  • 注意:参数只看类型是否相同,不看缺省值

多态构成的条件

  • 需要在继承中使用
  • 必须使用父类的引用或者指针调用虚函数
  • 被调用的函数必须是虚函数,且子类必须对父类的虚函数进行重写
回顾上面的代码,感受多态的条件
#include <iostream>
using namespace std;
class Person {
public:
    //父类的虚函数
	 virtual void BuyTicket() { cout << "成人买票-全价" << endl; }
};

class Student : public Person {
public:
    //子类 重写 父类的虚函数(重写:函数名、返回值、参数必须相同)
	virtual void BuyTicket() { cout << "学生买票-半价" << endl; }
};

void Func(Person* p)
{
	p->BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(&ps);
	Func(&st);
	return 0;
}

这时候指针p,它指向什么类型,就调用该类型的对象里面的成员

在这里插入图片描述

析构函数的重写-析构函数名统一会被处理成destructor()

只有子类 Student 的析构函数重写了 Person 的析构函数,下面的 delete 对象调用析构函
数,才能构成多态,才能保证 p1 和 p2 指向的对象正确的调用析构函数。
函数名处理成destructor() 才能满足多态:
如果父类的析构函数为虚函数,此时子类析构函数只要定义,无论是否加virtual关键字,
都与父类的析构函数构成重写,虽然父类与子类析构函数名字不同。虽然函数名不相同,
看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处
理,编译后析构函数的名称统一处理成destructor。

class Person {
public:
 virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:
 virtual ~Student() { cout << "~Student()" << endl; }
};
int main()
{
 Person* p1 = new Person;
 Person* p2 = new Student;
 delete p1;
 delete p2;
 return 0; 
}

在这里插入图片描述
所以当父类的指针指向子类的对象时,一定需要将父类的析构函数写成虚函数,如果不定义成虚函数,那么delete p的时候,就会只父类的析构函数(切片),不会调用子类的析构函数,造成内存泄漏

C++11 新语法:override 和 final

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

在这里插入图片描述

override: 检查子类虚函数是否重写了父类某个虚函数,如果没有重写编译报错

在这里插入图片描述

抽象类
1、概念

在虚函数的后面写上 “= 0” ,则这个函数为纯虚函数包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象子类继承后也不能实例化出对象,只有重写纯虚函数,子类才能实例化出对象。纯虚函数规范了子类必须重写,另外纯虚函数更体现出了接口继承。
在这里插入图片描述

2、 抽象类代码感知
#include <iostream>
using namespace std;

class Car
{
public:
	virtual void Drive() = 0;
	//	// 实现没有价值,因为没有对象会调用他
	//	/*virtual void Drive() = 0
	//	{
	//		cout << " Drive()" << endl;
	//	}*/
};
class Benz :public Car
{
public:
	virtual void Drive()  //重写父类的纯虚函数
	{
		cout << "Benz-舒适" << endl;
	}
};
class BMW :public Car
{
public:
	virtual void Drive() //重写父类的纯虚函数
	{
		cout << "BMW-操控" << endl; 
	}
};
void Test()
{
	Car* pBenz = new Benz;
	pBenz->Drive();
	Car* pBMW = new BMW;
	pBMW->Drive();
}
int main()
{
	Test();
	return 0;
}

在这里插入图片描述


总结

今天我们熟悉了多态的构成条件以及多态的使用,下一次我们详细聊聊多态的原理,感知一下为什么会出现这样的结果?编译器是怎么做得?

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值