C++——虚函数和多态

多态:
1.多态的概念
字面上理解:多种表现形式
专业术语:C++允许父类的指针或者父类引用指向不同的子类对象,通过这个指针或者引用去调用不同子类的同名方法——>叫做多态;
父类的同名函数在不同的子类中具有不同的表现形式——>叫做多态;

多态要解决的两个问题:
问题一:参数具有通用性
解决方法:C++允许父类的指针或者父类引用指向不同的子类对象(不需要做任何转换)
问题二:传递不同的子类,可以调用不同子类的同名函数

2.用虚函数来实现多态
虚函数的语法规则:在普通函数的前面加上关键字virtual
virtual 返回值 函数名(参数)
{
代码;
}

3.虚函数的特点,虚函数的底层原理
3.1 虚函数的特点
第一:一个类定义了虚函数,这个类的地址空间中会多出一个指针,该指针用来指向虚函数表的首地址
虚函数表(虚表):用来存放所有虚函数地址的一种数据结构你写的代码中所有的虚函数都是存储在这个表里面的

第二:父类的同名函数定义成虚函数,那么子类的同名也是虚函数,子类的同名函数virtual可以省略(可写可不写)

3.2 虚函数的底层原理(面试的时候喜欢问)
子类的同名虚函数会替换掉虚表中父类的同名虚函数
重写/复写:子类重新定义了父类的同名方法

4.重点:虚函数原理实现代码和内存变化演示
简单地说,有一些结论我们可以记住:

1.虚函数表就是一个指针数组,里面存放着函数地址指针。即每一个含有虚函数(无论是其本身的,还是继承而来的)的类都至少有一个与之对应的虚函数表,其中存放着该类所有的虚函数对应的函数指针。

2.父类定义了虚函数,子类的同名函数也是虚函数,但是子类的virtual可以省略不写。

3.父类创建一个指针指向虚函数表,表里面存储着父类的虚函数地址指针,子类不会再在创建指针,与父类共用一个指针。编译器发现子类复写了父类的同名函数,会将子类的同名函数地址替换掉虚函数表中的父类同名函数地址。

虚函数表和指针的结构关系如下图:




如何实现多态,实现多态有几个原则:
编程原则1:创建一个类的继承关系图,有一个共同的父类,例如此处的Person,如下图所示

编程原则2:创建一个数组,数组类型为所有类的共同父类的指针数组,然后将子类地址赋给父类指针变量

动态绑定

动态绑定与静态绑定的区别:静态绑定,程序在编译的时候,其运行结果事先就可以知道;动态绑定需运行至条件判断处,如上图所示,
若为真,则和Student绑定在一起;若为假,则和Professor绑定在一起;


在上图中Base b = d; 会发生数据损失,将子类转换成父类

虚析构函数


错误代码

#include<iostream>
using namespace std;

class Base 
{
public:
	~Base();		**// 此处应该加virtual**
};
Base::~Base() 
{
	cout << "Base destructor" << endl;
}
// 子类Derived
class Derived :public Base 
{
private:
	int* p;
public:
	Derived();
	~Derived();
};
Derived::Derived() 
{
	p = new int(0);
}
Derived::~Derived()
{
	cout << "Derived destructor" << endl;
	delete p;
}

void fun(Base* b)
{
	delete b;
}

int main() {
	Base* b = new Derived();    // new Derived()会先去调用子类的无参构造函数,在调用子类的无参构造函数之前,会去调用父类的无参构造函数
	fun(b);                    // 撤销的时候先去调用父类的析构函数,然后再去调用子类的析构函数
	return 0;
}

正确代码:

#include<iostream>
using namespace std;

class Base 
{
public:
	~Base();		// 此处应该加virtual
};
Base::~Base() 
{
	cout << "Base destructor" << endl;
}
// 子类Derived
class Derived :public Base 
{
private:
	int* p;
public:
	Derived();
	~Derived();
};
Derived::Derived() 
{
	p = new int(0);
}
Derived::~Derived()
{
	cout << "Derived destructor" << endl;
	delete p;
}

void fun(Base* b)
{
	delete b;
}

int main() {
	Derived d;
	return 0;
}

原文链接:https://blog.csdn.net/weixin_44651073/article/details/124480398

posted @   香花草的味道  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示