C++ 多态

本文详细探讨了C++中的多态,包括多态的构成条件、虚函数的重写、析构函数的重写、override和final的使用,以及重载、重定义和重写的区别。还深入介绍了抽象类、虚函数表、多继承中的虚函数表,并讨论了多态的原理和动态绑定。此外,文章还涉及菱形继承下的多态问题和多态相关题目。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、多态的定义和实现

1.1 多态的构成条件:

1.2 虚函数的重写(覆盖):

1.3 多态的两个特殊点:

1.4 析构函数的重写:

1.5 override和final

1.6 重载,重定义(隐藏),重写(覆盖)的区别

二、抽象类

2.1 纯虚函数

2.2 接口继承和实现继承

三、多态的原理

3.1 虚函数表

3.2 验证派生类自己新增的虚函数会不会存储在派生类虚表中:

3.3 多态的原理

3.4 动态绑定与静态绑定

四、多继承中的虚函数表

4.1 普通多继承下的虚函数表:

4.2 菱形继承下的多态

五、多态相关题


一、多态的定义和实现

1.1 多态的构成条件:

构成多态需要两个条件:

1. 必须通过基类的指针或者引用调用虚函数

2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

虚函数:被virtual修饰的类成员函数称为虚函数。

1.2 虚函数的重写(覆盖):

派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了父类的虚函数。 (其实就是和基类的虚函数声明完全相同,函数体不同,类似于重新定义函数的行为)

1.3 多态的两个特殊点:

1. 派生类中对基类虚函数进行重写时,可以省略virtual关键字,此时该函数仍然为虚函数且构成重写。建议加上virtual声明,更规范。(因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性)

2. 协变:派生类对基类虚函数进行重写时,要求函数名,参数列表,返回值都相同。一个例外是返回值类型可以不同,基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。

1.4 析构函数的重写:

回顾继承:

1. 在继承体系中,派生类的析构函数会自动调用基类析构函数去清理基类部分数据成员

2. 并且编译器会将继承体系中的析构函数函数名处理为destructor,所以若不将基类析构函数定义为虚函数,默认为隐藏关系!

那么,如果执行下面代码时,析构函数就必须重写!

class Base
{
public:
	Base()
		:p(new int[10])
	{
	}
	virtual ~Base()
	{
		cout << "~Base()" << endl;
		delete p;
	}
protected:
	int* p;
};

class Derived : public Base
{
public:
	Derived()  // 自动调用基类的默认构造函数
		:p2(new double[10])
	{
	}
	virtual ~Derived()
	{
		cout << "~Derived()" << endl;
		delete p2;
	}
protected:
	double* p2;
};

int main()
{
	Base* p = new Base;
	delete p;
	Base* p2 = new Derived;
	delete p2;
	return 0;
}

delete p2时,delete语句会执行 p2->destructor();   operator delete(ptr2); 调用p2所指向的析构函数,此时若不将析构函数定义为虚函数,则不会发生重写,而是隐藏。则第二个delete时,事实上,Derived类中会有两个析构函数,一个是基类的,一个是自己的。p2类型为Base*,且没有发生多态,只能调用基类的析构函数。

因此,诸如上方示例,建议将继承体系中的析构函数定义为虚函数,这样子类就可以对父类析构函数进行重写。

可以理解,编译后析构函数函数名被编译器处理为destructor(),也是为了便于设为虚函数,发生多态和动态绑定。这样delete基类指针时,才能调用正确的析构函数,即指向父类调用父类的析构函数,指向子类调用子类的析构函数。

1.5 override和final

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

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

1.6 重载,重定义(隐藏),重写(覆盖)的区别

重载:两个函数在同一定义域,函数名相同,参数列表不同。

重定义(隐藏):两个函数分别在基类和派生类的作用域中,函数名相同。

重写(覆盖):两个函数分别在基类和派生类的作用域中,函数名,参数,返回值相同(协变除外)

事实上,基类和派生类中,两个函数如果函数名相同,若没有构成重写,就是重定义(隐藏)

二、抽象类

2.1 纯虚函数

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

class Base
{
public:
    virtual void Drive() = 0;
};

2.2 接口继承和实现继承

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实 现,故,实际上调用基类的普通函数时,函数隐含的this指针类型为 Base* const。

虚函数的继承是一种接口继承,派生类继承的是

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值