C++ 多态 纯干货讲解 复制可调试(1)

💯 博客内容:多态

😀 作  者:陈大大陈

🚀 个人简介:一个正在努力学技术的准C++后端工程师,专注基础和实战分享 ,欢迎私信!

💖 欢迎大家:这里是CSDN,我总结知识和写笔记的地方,喜欢的话请三连,有问题请私信 😘 😘 😘

目录

虚函数继承的条件

普通调用

多态调用

例:

易错练习题

override 和 final

纯虚函数 

多态的本质

为什么对象不能实现多态?


 

虚函数继承的条件

1.虚函数重写

2.必须是父类的引用或者指针来调用 

普通调用

调用函数的类型是谁,就调用哪个类型的函数

多态调用

调用指针或者引用指向的对象,指向父类就调用父类,指向子类就调用子类。 

例:
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class Person
{
public:
	virtual void BuyTickets()
	{
		cout << "全价买票" << endl;
	}
};
class Student :public Person
{
	virtual void BuyTickets()
	{
		cout << "半价买票" << endl;
	}
};
void Func(Person& p)
{
	p.BuyTickets();
}
int main()
{
	Student st;
	Func(st);
	Person p;
	Func(p);

}

 上面的代码要是把引用去了,就是两个全价买票,因为普通调用只看参数类型。

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class Person
{
public:
	virtual void BuyTickets()
	{
		cout << "全价买票" << endl;
	}
	~Person()
	{
		cout << "~Person" << endl;
	}
};
class Student :public Person
{
public:
	virtual void BuyTickets()
	{
		cout << "半价买票" << endl;
	}
	~Student()
	{
		cout << "~Student" << endl;
	}
};
void Func(Person& p)
{
	p.BuyTickets();
}
int main()
{
	Student st;
	Func(st);
	Person p;
	Func(p);

}

一般情况下,析构没有问题。

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class Person
{
public:
	virtual void BuyTickets()
	{
		cout << "全价买票" << endl;
	}
	~Person()
	{
		cout << "~Person" << endl;
	}
};
class Student :public Person
{
public:
	virtual void BuyTickets()
	{
		cout << "半价买票" << endl;
	}
	~Student()
	{
		cout << "~Student" << endl;
	}
};
void Func(Person& p)
{
	p.BuyTickets();
}
int main()
{
	/*Student st;
	Func(st);
	Person p;
	Func(p);*/
	Person* ptr = new Person;
	delete ptr;
	ptr = new Student;
	delete ptr;

}

 在new的情况下就会有问题,因为析构没有满足多态的条件,所以析构是普通调用。

加上virtual即可。

接下来来看一道易错练习题。

易错练习题

 答案是B,容易选成D,需要注意的是,虚函数继承声明,重写实现。

派生类可以不加virtual,所以B的func接口继承了A。

所以val的值是1。

之后B对象切片后传参调用A的test,A的test调用B的func。

如果调用的语句是,p->func(),就不构成多态,答案会是B->0。

override 和 final

从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失,因此:C++11提供了override和final两个关键字,可以帮助用户检测是否重写。

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

2. override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

class Car

{ public:

virtual void Drive() final {}

};

class Benz :public Car

{ public:

virtual void Drive() {cout << "Benz-舒适" << endl;}

};


 

class Car{

public:

virtual void Drive(){}

};

class Benz :public Car {

public:

virtual void Drive() override {cout << "Benz-舒适" << endl;}

}; 

纯虚函数 

 在虚函数的后面写上 =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(); }
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class base
{
public:
	virtual void func1()
	{
		cout << "func1()" << endl;
	}

	virtual void func2()
	{
		cout << "func2()" << endl;
	}

	void func3()
	{
		cout << "func3()" << endl;
	}
private:
	int _b = 1;
	char _ch;
};

int main()
{
	cout << sizeof(base) << endl;
	base b;

	return 0;
}

虚函数里会存一个虚函数表指针来保存地址。

vfptr(virtual function pointer);

它是一个函数指针数组。 

父类和派生类的虚表指针不尽相同,重写了哪个虚函数,哪个虚表指针就会被覆盖。 

也就是说:

至此,我们明白了多态的本质

多态的本质

多态为什么能指向父类调父类,指向子类调子类?

虚函数的重写也叫虚函数的覆盖。

编译器首先在对象里找到虚函数表的地址。

查找虚函数表中存储的地址,是父类就调用父类,是子类就切割成父类对象。

是因为虚表指针会去寻找所调用的类的地址。 

为什么对象不能实现多态?

 因为指针和引用可以指向子类对象中切割出来的父类的一部分。

而对象的拷贝不会拷贝虚表指针。

如果拷贝虚表指针的话,父类和子类的虚函数以及析构函数极易混淆,造成报错。

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

陈大大陈

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

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

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

打赏作者

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

抵扣说明:

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

余额充值