C++进阶----多态

目录

引言

C++的三大的特性:封装、继承、多态

多态是一种面向对象编程的特性,它允许不同类的对象对同一消息做出响应。

在C++中,多态主要通过虚函数和抽象类来实现。虚函数允许派生类重写基类的方法,而抽象类则可以定义一个接口,强制派生类实现特定的方法。这样,基类指针或引用可以指向派生类对象,从而在运行时实现多态行为。

1.多态的概念

1.1概念

多态(Polymorphism)是面向对象编程(OOP)的一个基本概念,它允许同一个接口接受不同的数据类型。(简单来说就是去完成某个行为,当不同的对象去完成时会产生出不同的状态,比如火车票,成人买成人全价票,儿童买半票,学生买票享有学生的专属折扣票军人买票可以优先买票)

2.多态的定义及实现

2.1 多态的构成条件

多态是在不同继承关系的类对象,在这个前提下构成多态的俩个条件:

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

2.2虚函数

虚函数:被virtual修饰的类成员函数称为虚函数,无法通过自己本身来实例化

cpp 复制代码
class Person{
public:
	virtual void BuyTicket(){cout<<"买票-全价"<<endl;}
};

2.3 虚函数的重写

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

cpp 复制代码
#include<iostream>
using namespace std;
class father
{
public:
	virtual void Buysth(){ cout << "dad will take you shoping" << endl; }
};

class son:public father
{
public:
	virtual void Buysth(){ cout << "I will go shoping by meself" << endl; }
};

void fun(father& f)
{
	f.Buysth();
}

int main()
{
	father f;
	son s;
	f.Buysth();
	s.Buysth();

	fun(f);
	fun(s);//s的切片

	return 0;
}

虚函数重写的俩个例

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

基类虚函数返回基类对象的指

针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变

  1. 析构函数的重写

如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,

都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,

看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处

理,编译后析构函数的名称统一处理成destructor

2.4 关键字override和final

  1. find

关键字override用于显式指定派生类函数覆盖了基类的虚函数;final关键字则用于阻止一个虚函数在其派生类中被覆盖,或者阻止一个类被继承。

cpp 复制代码
class D{
public:
virtual void d() final{}//基类
}
  1. override

override关键字主要用于检查派生类的函数是否真正的重写了基类的虚函数,如果没有则会报错。调用覆盖的函数时,会调用派生类的版本,而不是基类的版本。

cpp 复制代码
class D{
public:
virtual void d ()voerride{}//派生类
}

2.5 重载、覆盖(重写)、隐藏对比

3.抽象类

3.1 抽象类概念

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。

抽象类是一个特殊的类,它是为了表示一种抽象的概念而设立的,不能被实例化。抽象类通常作为一种基本形态,由它派生出各种具体类。抽象类至少包含一个纯虚函数。

cpp 复制代码
class father
{
public:
	virtual void Buysth() =0 { cout << "dad will take you shoping" << endl; } 
};

class son:public father
{
public:
	virtual void Buysth(){ cout << "I will go shoping by meself" << endl; }
};

4.多态的原理

4.1 虚函数表

虚函数表是C++中的一个概念,用于支持多态性。每个具有虚函数的对象都有一个指向虚函数表的指针,虚函数表中包含了对象的虚函数的地址。

cpp 复制代码
class father
{
public:
	virtual void Buysth() { cout << "dad will take you shoping" << endl; }
private:
	int _x;
};
class A
{
public:
	void a(){}
private:
	int _x;
};
int main()
{
	father f;
	A a;
	return 0;
}

可以看出存在虚函数的类中,大小和普通类的大小是不一样的。

除了_x成员,还多一个__vfptr放在对象的前面(注意有些平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针(v代表virtual,f代表function

4.2虚函数表的底层

cpp 复制代码
class Base
{
public:
	virtual void fun1() { cout << "fun1()" << endl; }
	virtual void fun2() { cout << "fun2()" << endl; }
private:
	int _B = 0;
};
class A:public Base
{
public:
	void fun1() { cout << "A::fun1()" << endl; }
private:
	int _a = 1;
};

class B :public Base
{
public:
	void fun1() { cout << "B::fun1()" << endl; }
	void fun2() { cout << "B::fun2()" << endl; }
private:
	int _b = 2;
};

int main()
{
	Base base;
	A a;
	B b;
	return 0;
}

可以看出每个实例类的虚函数表的地址和所包括的虚函数都不一样,构成重写的会生成新的虚函数,而没有构成重写的会继承基类的虚函数

4.3多态的原理

多态的实现原理基于两个关键概念:虚函数表和虚函数表指针。每个多态类都有一张虚函数表,这张表中存储了该类的虚函数地址。每个多态类的对象都有一个虚函数表指针,该指针指向该类的虚函数表。当我们通过基类的指针调用虚函数时,程序会根据这个指针指向的虚函数表中的地址去调用对应的函数,从而实现了多态。

通过调用基类的引用和指针,派生类的指针和引用,来区别不继承类和基类的虚函数调用。满足多态以后的函数调用,不是在编译时确定的,是运行起来以后到对象的中取找的。不满足多态的函数调用时编译时确认好的

4.4 动态绑定和静态绑定

  1. 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为也称为静态多态

比如:函数重载

  1. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态

5.单继承和多继承关系中的虚函数表

5.1 单继承中的虚函数表

在4.2中的虚函数表底层中就是常见的单继承

5.2 多继承中的虚函数表

cpp 复制代码
class Base
{
public:
	virtual void fun1() { cout << "fun1()" << endl; }
	virtual void fun2() { cout << "fun2()" << endl; }
private:
	int _B = 0;
};

class More
{
public:
	virtual void fun1(){}
	virtual void fun2(){}
private:
	int _M = 0;
};

class A:public Base,public More
{
public:
	void fun1() { cout << "A::fun1()" << endl; }
private:
	int _a = 1;
};

int main()
{
	Base base;
	A a;
	return 0;
}

可以看出,多继承虚函数的派生类也会有多应多个的虚函数表(虚函数指针表)

6.总结

在C++中,多态性是一种允许不同类型的对象对同一消息做出不同响应的特性。通过虚函数和继承,我们可以实现运行时多态。运行时多态是面向对象编程的三大特性之一,它极大地增强了程序的可扩展性。但是,我们也要注意虚函数会带来一些性能开销,因此在使用时需要权衡利弊。

相关推荐
mozun20201 小时前
VS BUG(6) LINK : fatal error LNK1158: 无法运行“rc.exe”
c++·bug·vs·链接器·资源文件
夜夜敲码1 小时前
C语言教程(十八):C 语言共用体详解
c语言·开发语言
大学生亨亨2 小时前
go语言八股文(五)
开发语言·笔记·golang
raoxiaoya2 小时前
同时安装多个版本的golang
开发语言·后端·golang
whoarethenext2 小时前
初始https附带c/c++源码使用curl库调用
服务器·c++·qt·https·curl
我不会编程5553 小时前
Python Cookbook-6.10 保留对被绑定方法的引用且支持垃圾回收
开发语言·python
道剑剑非道3 小时前
QT开发技术【qcustomplot 曲线与鼠标十字功能】
开发语言·qt·计算机外设
刘婉晴4 小时前
【环境配置】Mac电脑安装运行R语言教程 2025年
开发语言·macos·r语言
Despacito0o4 小时前
C++核心编程:类与对象全面解析
开发语言·c++