C++:多态(继承)

news2024/9/8 7:10:55

hello,各位小伙伴,本篇文章跟大家一起学习《C++:多态》,感谢大家对我上一篇的支持,如有什么问题,还请多多指教 !

文章目录

    • :maple_leaf:多态的概念
    • :maple_leaf:继承中的多态
      • 1.:leaves:虚函数表
    • :maple_leaf:多态原理

🍁多态的概念

在 C++ 中,多态性(Polymorphism)是面向对象编程中一个重要的概念,它允许使用统一的接口来操作不同的对象,从而提高代码的灵活性、可维护性和可扩展性。多态性的实现依赖于两种主要机制:编译时多态性(静态多态性)和运行时多态性(动态多态性)

  1. 编译时多态性(静态多态性):
  2. 在 C++ 中,编译时多态性主要通过函数重载和运算符重载来实现。这种多态性是在编译期间根据函数或运算符的参数类型和数量来选择调用的函数版本,称为静态绑定或早期绑定
  3. 例如,函数重载允许在同一个作用域内定义多个函数名相同但参数列表不同的函数,编译器会根据调用时的参数类型来选择正确的函数。如下实现不同类型进行交换代码:
void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}

void Swap(double& left, double& right)
{
	double temp = left;
	left = right;
	right = temp;
}

void Swap(char& left, char& right)
{
	char temp = left;
	left = right;
	right = temp;
}
  1. 运行时多态性(动态多态性):
  2. 运行时多态性是通过虚函数和继承关系来实现的。这种多态性允许在程序运行时根据对象的实际类型来调用对应的函数,称为动态绑定或后期绑定。
  3. 在 C++ 中,通过在基类中将成员函数声明为虚函数(使用 virtual 关键字),允许派生类覆盖(override)这些虚函数。当通过基类指针或引用调用虚函数时,会根据实际指向的对象类型来决定调用哪个函数版本。

下面是一个简单的示例,展示了 C++ 中的运行时多态性:

#include <iostream>
using namespace std;

// Base class
class Animal {
public:
    // Virtual function
    virtual void speak() {
        cout << "Animal speaks!" << endl;
    }
};

// Derived class overriding the speak() function
class Dog : public Animal {
public:
    // Override the speak() function
    void speak() override {
        cout << "Dog barks!" << endl;
    }
};

// Derived class overriding the speak() function
class Cat : public Animal {
public:
    // Override the speak() function
    void speak() override {
        cout << "Cat meows!" << endl;
    }
};

int main() {
    Animal *animal;

    Dog myDog;
    Cat myCat;

    // Pointer to Dog object
    animal = &myDog;
    animal->speak();  // Output: Dog barks!

    // Pointer to Cat object
    animal = &myCat;
    animal->speak();  // Output: Cat meows!

    return 0;
}


在这个示例中:

  1. Animal 类中的 speak() 函数被声明为虚函数。
  2. Dog 和 Cat 类都重写了 speak() 函数,实现了不同的动作。
  3. 在 main() 函数中,通过 Animal 类指针 animal 分别指向 Dog 对象和 Cat 对象,并调用它们的 speak() 函数。虽然指针类型是基类 Animal,但实际上根据指向的对象类型,调用的是对应的虚函数版本,展示了运行时多态性的特性。

通过多态性,C++ 提供了一种灵活且强大的机制,使得程序能够根据对象的实际类型来决定调用哪个函数版本,从而实现代码的重用性和扩展性。

🍁继承中的多态

1.🍃虚函数表

先出一道题,32位机器下,sizeof(Base)答案是多少呢?

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

答案是:8
在这里插入图片描述
可以看到,除了_b成员,还多一个__vfptr放在对象的前面(注意有些平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针(v代表virtual,f代表function)。

class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "Base::Func2()" << endl;
	}
	void Func3()
	{
		cout << "Base::Func3()" << endl;
	}
private:
	int _b = 1;
};
class Derive : public Base
{
public:
	virtual void Func1()
	{
			cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};
int main()
{
	Base b;
	Derive d;
	return 0;
}
  1. 派生类对象d中也有一个虚表指针,d对象由两部分构成,一部分是父类继承下来的成员,虚表指针也就是存在部分的另一部分是自己的成员。
  2. 基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,所以d的虚表中存的是重写的Derive::Func1,所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法。
  3. 另外Func2继承下来后是虚函数,所以放进了虚表,Func3也继承下来了,但是不是虚函数,所以不会放进虚表。
  4. 虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个nullptr。
  5. 总结一下派生类的虚表生成:a.先将基类中的虚表内容拷贝一份到派生类虚表中 b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数 c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。
  6. 这里还有一个童鞋们很容易混淆的问题:虚函数存在哪的?虚表存在哪的? 答:虚函数存在虚表,虚表存在对象中。注意上面的回答的错的。但是很多童鞋都是这样深以为然的。注意虚表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚表中。另外对象中存的不是虚表,存的是虚表指针。

🍁多态原理

在CS2中,不同枪械的机动性不同:

class Gun
{
public:
	virtual void Mobility(Gun& gun) = 0;
};

class AK47 : public Gun
{
public:
	void Mobility(Gun& gun)
	{
		cout << "Mobility = " << _Mobility << endl;
	}
private:
	int _Mobility = 215;
};

class M4A4 : public Gun
{
public:
	void Mobility(Gun& gun)
	{
		cout << "Mobility = " << _Mobility << endl;
	}
private:
	int _Mobility = 225;
};

void Func(Gun& gun)
{
	gun.Mobility(gun);
}

int main()
{
	AK47 ak47;
	M4A4 m4a4;

	Gun& ak = ak47;
	Gun& m4 = m4a4;

	Func(ak);
	Func(m4);

	return 0;
}

在这里插入图片描述
在这里插入图片描述

  • 多态必须要满足两个条件:
    1.必须通过基类的指针或者引用调用虚函数
    2.被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

举个简单例子解释,如下代码:

class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "Base::Func2()" << endl;
	}
	void Func3()
	{
		cout << "Base::Func3()" << endl;
	}
private:
	int _b = 1;
};
class Derive : public Base
{
public:
	virtual void Func1()
	{
			cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};

int main()
{
	Base b;
	Derive d;
	Base& s1 = b;
	Base& s2 = d;

	s1.Func1();
	s2.Func1();
	return 0;
}

解释:Derive继承BaseFunc1进行重写,重写后的虚函数会产生新的地址,放入虚表中Base&引用子类Derive对象,会发生切割,切割部分其实就是继承Base部分,由于DeriveFunc1进行重写,所以s2虚表中的Func1是已经重写的。

图解:
在这里插入图片描述
在这里插入图片描述
你学会了吗?
好啦,本章对于《C++:多态(继承)》的学习就先到这里,如果有什么问题,还请指教指教,希望本篇文章能够对你有所帮助,我们下一篇见!!!

如你喜欢,点点赞就是对我的支持,感谢感谢!!!

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1912036.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BKP备份寄存器和实时时钟笔记

BKP&#xff08;Backup Registers&#xff09;备份寄存器 BKP可用于存储用户应用程序数据。当VDD&#xff08;2.0~3.6V&#xff09;电源被切断&#xff0c;他们仍然由VBAT&#xff08;1.8~3.6V&#xff09;维持供电。当系统在待机模式下被唤醒&#xff0c;或系统复位或电源复位…

Linux 网络--TCP协议收包流程(NAPI机制)

Linux 网络--TCP协议收包流程&#xff08;NAPI机制&#xff09; 平台环境简介&#xff1a;宿主机: ubuntu18.04Linux内核源码版本: Linux-4.15网卡驱动: Intel e1000 &#xff08;ubuntu 虚拟机默认网卡驱动&#xff09;协议&#xff1a;TCP协议&#xff0c;本文分析收包过程 本…

Django 更新数据 save()方法

1&#xff0c;添加模型 Test/app11/models.py from django.db import modelsclass Post(models.Model):title models.CharField(max_length200)content models.TextField()pub_date models.DateTimeField(date published)class Book(models.Model):title models.CharFie…

9.pwn 栈溢出

栈溢出简介 函数中的存储在栈中的局部变量数组边界检查不严格发生越界写&#xff0c;造成用户输入覆盖到缓冲区外的数据内容&#xff0c; 由于栈中同时存在着与函数调用参数的相关信息&#xff0c;栈溢出可以导致控制流劫持 基础栈溢出(hello world in pwn) 多数情况下我们需…

【 正己化人】 把自己做好,能解决所有问题

阳明先生说&#xff1a;与朋友一起辩论学问&#xff0c;纵然有人言辞观点浅近粗疏&#xff0c;或者是炫耀才华、显扬自己&#xff0c;也都不过是毛病发作。只要去对症下药就好&#xff0c;千万不能怀有轻视别人的心理&#xff0c;因为那不是君子与人为善的心。 人会爱发脾气、…

UE5.3-基础蓝图类整理一

常用蓝图类整理&#xff1a; 1、获取当前关卡名&#xff1a;Get Current LevelName 2、通过关卡名打开关卡&#xff1a;Open Level(by name) 3、碰撞检测事件&#xff1a;Event ActorBeginOverlap 4、获取当前player&#xff1a;Get Player Pawn 5、判断是否相等&#xff1…

阿里云人工智能平台PAI部署开源大模型chatglm3之失败记录--update:最后成功了!

想学习怎么部署大模型&#xff0c;跟着网上的帖子部署了一个星期&#xff0c;然而没有成功。失败的经历也是经历&#xff0c;记在这里。 我一共创建了3个实例来部署chatglm3&#xff0c;每个实例都是基于V100创建的&#xff08;当时没有A10可选了&#xff09;&#xff0c;其显…

自动化测试之unittest框架详解

1、什么是Unittest框架&#xff1f; python自带一种单元测试框架 2、为什么使用UnitTest框架&#xff1f; >批量执行用例 >提供丰富的断言知识 >可以生成报告 3、核心要素 1).TestCase&#xff08;测试用例&#xff09; 2).TestSuite(测试套件) 3).Test…

比赛获奖的武林秘籍:05 电子计算机类比赛国奖队伍技术如何分工和学习内容

比赛获奖的武林秘籍&#xff1a;05 电子计算机类比赛国奖队伍技术如何分工和学习内容 摘要 本文主要介绍了在电子计算机类比赛中技术层面上的团队分工和需要学习的内容&#xff0c;分为了嵌入式硬件、嵌入式软件、视觉图像处理、机械、上位机软件开发和数据分析等六个方向&am…

ORA-12537: TNS:连接关闭/Io 异常: Got minus one from a read call

在另外一个数据库建立dblink的时候&#xff0c;发现执行命令报错&#xff1a; 被连接的数据库我也上去过&#xff0c;用工具尝试登陆也报错&#xff1a; IO Error: Got minus one from a read call, connect lapse 1 ms., Authentication lapse 0 ms. Got minus one from a …

PTA - 编写函数计算圆面积

题目描述&#xff1a; 1.要求编写函数getCircleArea(r)计算给定半径r的圆面积&#xff0c;函数返回圆的面积。 2.要求编写函数get_rList(n) 输入n个值放入列表并将列表返回 函数接口定义&#xff1a; getCircleArea(r); get_rList(n); 传入的参数r表示圆的半径&#xff0c…

新闻资讯整合平台:一站式满足企业信息需求

摘要&#xff1a; 面对信息爆炸的时代&#xff0c;企业如何在海量数据中快速获取有价值资讯&#xff0c;成为提升竞争力的关键。本文将探讨如何通过一站式新闻资讯整合平台&#xff0c;实现企业信息需求的全面满足&#xff0c;提升决策效率&#xff0c;同时介绍实用工具推荐&a…

如何构建数据驱动的企业?爬虫管理平台是关键桥梁吗?

一、数据驱动时代&#xff1a;为何选择爬虫管理平台&#xff1f; 在信息爆炸的今天&#xff0c;数据驱动已成为企业发展的核心战略之一。爬虫管理平台&#xff0c;作为数据采集的第一站&#xff0c;它的重要性不言而喻。这类平台通过自动化手段&#xff0c;从互联网的各个角落…

AI Earth——1990-2022年全国月度气象数据检索应用app

应用结果 代码 #导入安装包 import os import json import datetime import streamlit as st import streamlit.components.v1 as components import traceback from PIL import Imageimport aie#读取当前目录的内容 current_work_dir = os.path.dirname(__file__) #添加地图…

PolarisMesh源码系列——服务端启动流程

前话 PolarisMesh&#xff08;北极星&#xff09;是腾讯开源的服务治理平台&#xff0c;致力于解决分布式和微服务架构中的服务管理、流量管理、配置管理、故障容错和可观测性问题&#xff0c;针对不同的技术栈和环境提供服务治理的标准方案和最佳实践。 PolarisMesh 官网&am…

开发个人Go-ChatGPT–6 OpenUI

开发个人Go-ChatGPT–6 OpenUI Open-webui Open WebUI 是一种可扩展、功能丰富且用户友好的自托管 WebUI&#xff0c;旨在完全离线运行。它支持各种 LLM 运行器&#xff0c;包括 Ollama 和 OpenAI 兼容的 API。 功能 由于总所周知的原由&#xff0c;OpenAI 的接口需要密钥才…

网络安全——防御实验

防御实验一 拓扑结构展示&#xff1a; 一、 根据题目&#xff0c;先为办公区做安全策略主要策略有以下几点&#xff1a; 1、书写名称和描述&#xff0c;名称和描述要明确&#xff0c;让除本人以外的人也能理解 2、确定源地址为办公区&#xff0c;目标地址为DMZ区 3、确定时间…

QT程序异常结束解决方法

在用QT开发第三方SDK的时候&#xff0c;刚开始是运行正常的&#xff0c;但是重装系统之后再次运行程序总是出现&#xff1a;程序异常结束。 以下方法尝试无效&#xff0c;但不失为一种排查方法&#xff1a; 重新安装QT&#xff1b;检查Qt Creator配置&#xff0c;编译器位数和…

java LogUtil输出日志打日志的class文件内具体方法和行号

最近琢磨怎么把日志打的更清晰&#xff0c;方便查找问题&#xff0c;又不需要在每个class内都创建Logger对象&#xff0c;还带上不同的颜色做区分&#xff0c;简直不要太爽。利用堆栈的方向顺序拿到日志的class问题。看效果&#xff0c;直接上代码。 1、demo test 2、输出效果…

什么是O2O?线上线下怎么完美结合?

现如今互联网技术飞速发展&#xff0c;智能手机普及。O2O&#xff08;Online To Offline&#xff09;模式已经成为一种新的商业模式&#xff0c;人们的生活和消费习惯也逐渐被改变。经常听到企业提到“O2O”&#xff0c;它究竟是什么呢&#xff1f;对企业有着什么魅力呢&#x…