深入解析C++中的虚函数和虚继承:实现多态性与继承关系的高级特性

news2024/12/30 1:51:58

这里写目录标题

  • 虚函数
  • 虚函数实现动态绑定
  • 虚继承
  • 抽象类

虚函数

虚函数是在C++中用于实现多态性的一种特殊函数。它通过使用关键字"virtual"进行声明,在基类中定义,可在派生类中进行重写。虚函数允许在运行时根据对象的实际类型来调用相应的函数,而不仅仅根据指针或引用的类型。这使得在继承关系中,通过基类指针或引用调用虚函数时,可以根据实际对象的类型来动态地确定要执行的函数版本,实现多态性的特性。

虚函数定义

在 C++ 中,可以通过在函数声明前面加上关键字 virtual 来定义虚函数。例如:

class Base {
public:
    virtual void virtualFunction() {
        // 函数定义
    }
};

在这个例子中,virtualFunction 被声明为虚函数。派生类可以重写这个虚函数,实现多态性。

虚函数实现动态绑定

动态绑定(Dynamic Binding),也称为运行时多态性(Runtime Polymorphism),是通过在基类和派生类中使用虚函数来实现的。

在C++中,当基类的指针或引用指向派生类的对象时,通过调用虚函数,可以实现对应于派生类的特定实现。这种根据对象的实际类型来确定调用哪个函数的机制就是动态绑定。

动态绑定的关键在于使用virtual关键字将成员函数声明为虚函数,并在基类和派生类中提供相应的实现。

#include<iostream>
#include <cstring>
using namespace std;
class cemployee
{
    public:
        int m_id;
        char name[10];
        cemployee()
        {
            memset(name,0,10);
        }
      virtual  void outputname()
        {
            cout<<"employee name:"<<name<<endl;
        }
};
class comployee:public cemployee
{
    public:
        char password[10];
        void outputname()
        {
            cout<<"opertor name:"<<name<<endl;
        }
};        
int main()
{
    cemployee* pworker = new comployee();
    strcpy(pworker->name,"MP");
    pworker->outputname();
    delete pworker;


    return 0;
}

动态多态满足关系:
1.有继承关系
2.子类重写父类的虚函数
动态多态使用:父类的指针或引用 指向子类对象
重写:函数返回值类型 函数名 参数列表 完全一致叫重写

如果子类中没有堆区数据,可以不用写虚析构和纯虚析构。

1.虚析构与纯虚析构共性:
解决父类指针释放子类对象不干净问题
都需要有具体的函数实现

2.区别:
如果是纯虚析构,该类属于抽象类,无法实例化
.虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名()=0;
类名::~类名(){}

纯虚数
子类的内容会覆盖父类,所以父类中函数没有意义了
类中只要有一个纯虚函数就称为抽象类
virtual void func() = 0;
抽象类无法实例化对象(堆区,栈区)
子类也必须要重写父类中的虚函数,否则子类也就是抽象类

具体代码示意如下所示

#include<iostream>
using namespace std;

class animal
{
public:
	void speak()
	{
		cout << "dongwuzaishouhua" << endl;
	}

	 void speak()
	{
		cout << "dongwuzaishouhua" << endl;
	}
	
class cat :public animal
{
public:
	void speak()
	{
		cout << "xiaomaozaishuohua" << endl;
	}
};
class dog :public animal
{
public:

	virtual void speak()
	{
		cout << "xioagouzaishuoihua" << endl;
	}
	//注释之后对象模型:

	class dog       size(1):
        +---
 0      | +--- (base class animal)
        | +---
        +---

};

void dospeak(animal& animal) //aniaml& aniaml= cat
{
	animal.speak();       //会打印出dongwuzaishouhua,因为aniaml& 
}
void test01()
{
	cat cat;
	dospeak(cat);
	dog dog;
	dospeak(dog);
}
/*
vfptr: 虚函数表指针
v- virtual
f- function
ptr- pointor
vftable:虚函数表
v- virtual
f- function
table- table
*/
![请添加图片描述](https://img-blog.csdnimg.cn/direct/0194baae48ad4111aa4acc0ce9f011df.png)

/* 有函数对象时模型:
class dog       size(4):
		+---
 0      | +--- (base class animal)
 0      | | {vfptr}
		| +---
		+---

dog::$vftable@:
		| &dog_meta
		|  0
 0      | &dog::speak  //覆盖 父类的指针或引用 指向子类对象发生多态

*/
class base
{
public:
	//纯虚数
	// 子类的内容会覆盖父类,所以父类中函数没有意义了
	//类中只要有一个纯虚函数就称为抽象类
	virtual void func() = 0;
	
	/*
        抽象类无法实例化对象(堆区,栈区)
		子类也必须要重写父类中的虚函数,否则子类也就是抽象类
	*/
	virtual ~base()
	{
		cout << "base的析构函数" << endl;
	}

};


class son :public base
{
public:
	virtual void func()
	{
		cout << "fff";
	}
	virtual ~son()
	{
		cout << "son的析构函数" << endl;
	}

};
void test00()
{
	//son s;不允许使用抽象类类型 "son" 的对象
	//base s;
	//new base;
	/*base* b = new son;
	b->func();*/


}
int main()
{

	//test01();
	test00();
	system("pause");
	return 0;
}

虚继承

请添加图片描述

虚继承(Virtual Inheritance)是C++中的一种继承方式,用于解决多继承中的菱形继承问题。

在多重继承中,如果一个派生类从两个或更多的基类继承,而这些基类又共同继承自同一个基类,就会出现菱形继承问题。这种情况下,派生类会包含同一个基类的多份拷贝,导致二义性和内存浪费。

虚继承通过使用virtual关键字修饰基类,在派生类对该基类进行继承时,确保只有一份共享的基类子对象被创建,从而解决了菱形继承问题。

#include<iostream>
using namespace std;
class cnaimal
{
    public:
        cnaimal(){
        cout<<"animal was created"<<endl;
        }
        void move(){
            cout<<"animal could moving"<<endl;
        }
};
class cbird:public cnaimal
{
    public:
        cbird(){
            cout<<"bird was created"<<endl;
        }
        void fly(){
            cout<<"bird would flying"<<endl;
        }
};
class cfish:public cnaimal
{
    public:
        cfish(){
            cout<<"fish was created"<<endl;
        }
        void swim(){
            cout<<"fish would swim"<<endl;
        }
};
class cwaterbird: public cbird, public cfish
{
    public:
        cwaterbird(){
            cout<<"cwaterbird was created"<<endl;
        }
};
int main()
{
    cwaterbird waterbird;
    
    return 0;
}

运行结果·:

[bsk@localhost polymorphic]$ g++ virtualinheritance.cpp 
[bsk@localhost polymorphic]$ ./a.out 
animal was created
bird was created
animal was created
fish was created
cwaterbird was created

我们可以看到,当只创建一个cwaterbird类时,由于它继承于cbird类和cfish类,所以会先去调用他们的构造函数,但是他们又继承于canimal类,于是又先去调用animal was created,然后再是鸟类的自身构造,bird was created,鸟类构造完之后,又来构造鱼类,同鸟类一样,先是animal was created,再是自身构造,fish was created,最后才是cwaterbird was created

#include<iostream>
using namespace std;
class cnaimal
{
    public:
        cnaimal(){
        cout<<"animal was created"<<endl;
        }
        void move(){
            cout<<"animal could moving"<<endl;
        }
};
class cbird:virtual public cnaimal
{
    public:
        cbird(){
            cout<<"bird was created"<<endl;
        }
        void fly(){
            cout<<"bird would flying"<<endl;
        }
};
class cfish:virtual public cnaimal
{
    public:
        cfish(){
            cout<<"fish was created"<<endl;
        }
        void swim(){
            cout<<"fish would swim"<<endl;
        }
};
class cwaterbird: public cbird, public cfish
{
    public:
        cwaterbird(){
            cout<<"cwaterbird was created"<<endl;
        }
};
int main()
{
    cwaterbird waterbird;
    
    return 0;
}

结果·如下所示:

[bsk@localhost polymorphic]$ ./a.out 
animal was created
bird was created
fish was created
cwaterbird was created

可见,当类是虚继承时,我们可以发现animal类的构造就只有一个了,
请添加图片描述

抽象类

抽象类包含有纯虚函数的类,一个抽象类至少有一个纯虚函数。抽象类只能作为基类派生出的新子类,而不能在程序中被实例化(不能声明抽象类的对象),但是可以指向抽象类的指针。

纯虚函数(Pure Virtual Function)是在基类中声明的没有实际实现的虚函数。它通过在函数声明后面加上= 0来表示。

纯虚函数在基类中起到了接口的定义作用,要求派生类必须提供对应的实现。如果一个类包含了纯虚函数,那么它就成为了抽象类,无法被直接实例化,只能作为基类来派生其他类。

纯虚函数使用的语法如下:

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

在上述示例中,Base类中的pureVirtualFunction函数被声明为纯虚函数。该函数没有实际的实现,只是作为接口的定义存在。

派生类必须提供对纯虚函数的实现,否则它们也会成为抽象类。一个派生类可以选择重写纯虚函数,也可以将其继续声明为纯虚函数,这取决于派生类是否需要进一步派生。

一个简单的示例:

#include <iostream>

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

class Derived : public Base {
public:
    void pureVirtualFunction() override {
        std::cout << "Derived class implementation." << std::endl;
    }
};

int main() {
    // Base base;  // 错误,无法实例化抽象类
    Derived derived;
    derived.pureVirtualFunction();  // 调用Derived类的实现

    Base* basePtr = &derived;
    basePtr->pureVirtualFunction();  // 通过基类指针调用Derived类的实现

    return 0;
}

在上述示例中,Base类中的pureVirtualFunction函数被声明为纯虚函数。Derived类继承自Base类,并提供了对纯虚函数的具体实现。通过Derived类的对象或基类指针可以调用纯虚函数的具体实现。

纯虚函数允许在基类中定义一组接口,并强制要求派生类提供相应的实现。它是实现抽象类和多态性的重要机制之一。
如果某个函数不是抽象类中的成员函数,不能用基类指针调用。

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

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

相关文章

FTR223限时回归?经典三花再加金翅膀,CL500特别款亮相

FTR223可以说是非常经典的一款本田小攀爬车型了&#xff0c;之前我还有幸玩过一段时间&#xff0c;最近本田在泰国车展上展出了CL500的特别版&#xff0c;其中FTR223纪念版的版画让人眼前一亮&#xff0c;经典的白、红、蓝三色搭配让人眼前一亮。 CL500这台车在国内今年刚上市&…

开关量防抖滤波器(梯形图和SCL源代码)

模拟量防抖超限报警功能块请查看下面文章链接: https://rxxw-control.blog.csdn.net/article/details/133969425https://rxxw-control.blog.csdn.net/article/details/133969425 1、开关量防抖滤波器 2、防抖滤波 3、梯形图代码

设计模式(二)-创建者模式(5)-建造者模式

一、为何需要建造者模式&#xff08;Builder&#xff09;? 在软件系统中&#xff0c;会存在一个复杂的对象&#xff0c;复杂在于该对象包含了很多不同的功能模块。该对象里的各个部分都是按照一定的算法组合起来的。 为了要使得复杂对象里的各个部分的独立性&#xff0c;以及…

透析跳跃游戏

关卡名 理解与贪心有关的高频问题 我会了✔️ 内容 1.理解跳跃游戏问题如何判断是否能到达终点 ✔️ 2.如果能到终点&#xff0c;如何确定最少跳跃次数 ✔️ 1. 跳跃游戏 leetCode 55 给定一个非负整数数组&#xff0c;你最初位于数组的第一个位置。数组中的每个元素代表…

轻松构建超市管理小程序

随着科技的发展&#xff0c;越来越多的超市开始使用管理系统来提高效率、提升顾客体验和管理库存。如果你也想要为自己的超市打造一个便捷、高效的小程序&#xff0c;下面将为你提供一些帮助。 首先&#xff0c;你需要打开乔拓云第三方平台。在这个平台上&#xff0c;你可以轻松…

云降水物理基础

云降水物理基础 云的分类 相对湿度变化方程 由相对湿度的定义&#xff0c;两边取对数之后可以推出 联立克劳修斯-克拉佩龙方程&#xff08;L和R都为常数&#xff09; 由右式看出&#xff0c;增加相对湿度的方式&#xff1a;增加水汽&#xff08;de增大&#xff09;和降低…

【后端学前端】第二天 css动画 动感菜单(css变量、过渡动画、过渡延迟、js动态切换菜单)

目录 1、学习信息 2、源码 3、变量 1.1 定义变量 1.2 使用变量 1.3 calc() 函数 4、定位absolute和fixed 5、transform 和 transition&#xff0c;动画 5.1 变形transform 5.2 transition 5.3 动画animation 6、todo 1、学习信息 视频地址&#xff1a;css动画 动感菜…

大一作业习题

第一题&#xff1a;答案&#xff1a; #include <stdio.h> void sort(int a[], int m) //将数组a的前m个元素(从小到大)排序 {int i 0;for (i 0; i < m - 1; i){int j 0;int flag 1;for (j 0; j < m - 1 - i; j){if (a[j] > a[j 1]){int t 0;t a[j];…

数据科学实践:探索数据驱动的决策

写在前面 你是否曾经困扰于如何从海量的数据中提取有价值的信息?你是否想过如何利用数据来指导你的决策,让你的决策更加科学和精确?如果你有这样的困扰和疑问,那么你来对了地方。这篇文章将引导你走进数据科学的世界,探索数据驱动的决策。 1.数据科学的基本原则 在我们…

AGM离线下载器使用说明

AGM专用离线下载器示意图&#xff1a; 供电方式&#xff1a; 通过 USB 接口给下载器供电&#xff0c;跳线 JP 断开。如果客户 PCB 的 JTAG 口不能提供 3.3V 电源&#xff0c;或仅需烧写下载器&#xff0c;尚未连接用户 PCB 时&#xff0c;采用此种方式供电。 或者&#xff1a…

测距传感器

测距传感器 电子元器件百科 文章目录 测距传感器前言一、测距传感器是什么二、测距传感器的类别三、测距传感器的应用实例四、测距传感器的作用原理总结前言 测距传感器广泛应用于自动化控制、机器人导航、无人驾驶、测量仪器等领域。不同类型的测距传感器具有不同的测距范围、…

从零开始实现神经网络(三)_RNN循环神经网络

参考文章&#xff1a;rnn循环神经网络介绍 循环神经网络 &#xff08;RNN&#xff09; 是一种专门处理序列的神经网络。它们通常用于自然语言处理 &#xff08;NLP&#xff09; 任务&#xff0c;因为它们在处理文本方面很有效。在这篇文章中&#xff0c;我们将探讨什么是 RNN&a…

【ClickHouse】ClickHouse与MySQL之间实时同步数据(MySQL引擎),将MySQL数据实时同步到clickhouse

参考1:MySQL(通过该配置实现了实时同步) 参考2:experimental MaterializedMySQL 参考3:[experimental] MaterializedMySQL(包含设置 allow_experimental_database_materialized_mysql) MySQL引擎用于将远程的MySQL服务器中的表映射到ClickHouse中&#xff0c;并允许您对表进行I…

GitHub 跑了 1200 多台 MySQL 主机,如何实现无缝升级到 8.0 版本?

文章目录 翻译概述前言升级的动机GitHub 的 MySQL 基础设施准备旅程准备基础设施以进行升级确保应用程序兼容性沟通和透明度升级计划第 1 步&#xff1a;滚动副本升级步骤 2&#xff1a;更新复制拓扑步骤 3&#xff1a;将 MySQL 8.0 主机提升为主主机步骤 4&#xff1a;升级面向…

【Linux】地址空间

本片博客将重点回答三个问题 什么是地址空间&#xff1f; 地址空间是如何设计的&#xff1f; 为什么要有地址空间&#xff1f; 程序地址空间排布图 在32位下&#xff0c;一个进程的地址空间&#xff0c;取值范围是0x0000 0000~ 0xFFFF FFFF 回答三个问题之前我们先来证明地址空…

openlayers-19-分屏对比

分屏对比实现很简单&#xff0c;定义两个map对象&#xff0c;然后让这两个map对象共用一个view即可。 代码如下&#xff1a; <!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd…

Linux上编译和测试V8引擎源码

介绍 V8引擎是一款高性能的JavaScript引擎&#xff0c;广泛应用于Chrome浏览器和Node.js等项目中。在本篇博客中&#xff0c;我们将介绍如何在Linux系统上使用depot_tools工具编译和测试V8引擎源码。 步骤一&#xff1a;安装depot_tools depot_tools是一个用于Chromium开发…

【深度学习】强化学习(五)深度强化学习

文章目录 一、强化学习问题1、交互的对象2、强化学习的基本要素3、策略&#xff08;Policy&#xff09;4、马尔可夫决策过程5、强化学习的目标函数6、值函数7、深度强化学习1. 背景与动机2. 关键要素3. 成功案例4. 挑战和未来展望5. 核心概念和方法总结 一、强化学习问题 强化学…

微服务网关组件Gateway实战

1. 需求背景 在微服务架构中&#xff0c;通常一个系统会被拆分为多个微服务&#xff0c;面对这么多微服务客户端应该如何去调用呢&#xff1f;如果根据每个微服务的地址发起调用&#xff0c;存在如下问题&#xff1a; 客户端多次请求不同的微服务&#xff0c;会增加客户端代码…

【LLM】大模型之RLHF和替代方法(DPO、RAILF、ReST等)

note SFT使用交叉熵损失函数&#xff0c;目标是调整参数使模型输出与标准答案一致&#xff0c;不能从整体把控output质量&#xff0c;RLHF&#xff08;分为奖励模型训练、近端策略优化两个步骤&#xff09;则是将output作为一个整体考虑&#xff0c;优化目标是使模型生成高质量…