C++多态

1、多态的概念

1.1概念引入:

我们在日常去景点、坐火车等地方,都需要买票,而对成年人是要求购买全价票。对学生而言需要购买半价票。这个场景是怎么样实现的呢?怎么同样是一个人,如何能清晰的区分该购买哪一种价格的票呢?

C++中,我们引入多态这个概念,通俗的讲,多态就是多种形态;当去完成某个行为的时候,不同的对象完成会产生不同的结果。

所以,对于买票而言,我们成年人去买,后台会让给我们购买全票,学生去买,只会买半价票。

2、多态的实现

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如 Student 继承了
Person Person 对象买票全价, Student 对象买票半价。

2.1虚函数

虚函数:即被 virtual 修饰的类成员函数称为虚函数。
class Person {
public:
 virtual void BuyTicket() { cout << "买票-全价" << endl;}
};

虚函数的重写

虚函数的重写也叫覆盖, 派生类中有一个跟基类完全相同的虚函数 ( 即派生类虚函数与基类虚函数的 返回值类型、函数名字、参数列表完全相同,简称三同 ) ,称子类的虚函数重写了基类的虚函数。

2.2构成条件

2.2.1多态有两个构成的必要条件

1. 必须通过基类的指针或者引用调用虚函数(只能是基类的指针或者引用,派生类不行)
2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
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);//传子类对象调用子类
	//st传给p——切割出子类中父类那部分,去调用子类的虚函数
	return 0;
}

另外需要注意,基类虚函数的virtual是不能省略的,省略就不会构成多态派生类的虚函数中virtual可以省略,因为虚函数的重写本质是对基类虚函数实现部分的重写,函数声明会继承下来。

2.2.2虚函数重写的两个例外

1.协变——基类与派生类虚函数返回值类型不同

派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指
针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
也就是说派生类虚函数返回类型(指针或引用)如果是基类的派生类的话仍构成多态。
例如:
class A 
{};
class B : public A 
{};
class Person {
public:
	virtual A* f() 
	{
		return new A;
	}
};
class Student : public Person {
public:
	virtual B* f() //基类和派生类虚函数返回类型不一样,但B是A的子类,所以仍然构成多态
	{
		return new B;
	}
};

2.析构函数的重写

如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加 virtual 关键字,
都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,
看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处
理,编译后析构函数的名称统一处理成 destructor
注意:只要基类的析构函数是虚函数,那么派生类的析构都会重写
class Person {
public:
	virtual ~Person()//编译后析构函数名字统一都叫destructor
	{ 
		cout << "~Person()" << endl;
	}
};
class Student : public Person {
public:
	virtual ~Student() //编译后析构函数名字统一都叫destructor
	{ 
		cout << "~Student()" << endl;
	}
};
int main()
{
	Person* p1 = new Person;
	Person* p2 = new Student;
	delete p1;//p1->~Person();
	delete p2;//p2->~Student();
	return 0;
}

C++11提供了两个关键字,final和override;

final修饰的类表示该类不能被继承,final修饰的虚函数,表示该虚函数不能被子类重写;

override修饰派生类的虚函数,表示检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

区分对比函数重载、隐藏、重写

3、抽象类

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。 包含纯虚函数的类叫做抽象类(也叫接口
类),抽象类不能实例化出对象 。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生
类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
class Car
{
public:
	virtual void Drive() = 0;
};
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();
}

例如这个Car类,车是一个大类,它并不能实例化出什么对象,没有任何意义。这个时候我们在它的虚函数的后面加上=0,让它成为一个抽象类。

抽象类的意义是强制让派生类重写虚函数,因为如果不重写的话,子类依旧是抽象类。

我们上面提到虚函数的重写是对实现部分的重写,所以它是一个接口继承。而我们普通函数继承的是实现,叫实现继承。

4、多态的原理

class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}
private:
	int _b = 1;
};
int main()
{
	cout << sizeof(Base) << endl;
	return 0;
}

这个类的大小是多少(32位)下,大家肯定觉得是4字节,其实不然

很多人疑惑,为什么是8呢?其实类里面有虚函数,创建的对象里面还有一个虚表指针_vfptr

那这个是什么多西呢?虚表指针?虚表的地址?虚表又是什么?其实虚表是虚函数数组,这个数组里面存放的是虚函数的地址。因为能力有限,所以我认为我把虚表指针的底层给大家讲不明白,推荐大家自己在老师讲课的时候重点注意这块,或者自己在网上找一些课程研究,实在是对我来是目前还没有掌握透彻,抱歉啦。

相关推荐

  1. <span style='color:red;'>C</span>++<span style='color:red;'>多</span><span style='color:red;'>态</span>

    C++

    2024-02-05 04:40:01      48 阅读
  2. 八股文 c++

    2024-02-05 04:40:01       44 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-05 04:40:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-05 04:40:01       101 阅读
  3. 在Django里面运行非项目文件

    2024-02-05 04:40:01       82 阅读
  4. Python语言-面向对象

    2024-02-05 04:40:01       91 阅读

热门阅读

  1. 记录 | python .ui转py

    2024-02-05 04:40:01       49 阅读
  2. 【深度学习】P1 Deep Learning 简介

    2024-02-05 04:40:01       57 阅读
  3. js custom-decorator 自定义装饰器,快速创建装饰器

    2024-02-05 04:40:01       54 阅读
  4. 【LeetCode-56】合并区间(贪心)

    2024-02-05 04:40:01       51 阅读
  5. 设计模式——七大原则

    2024-02-05 04:40:01       43 阅读