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

这里写目录标题

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

虚函数

虚函数是在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类的对象或基类指针可以调用纯虚函数的具体实现。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在HarmonyOS上使用ArkUI实现计步器应用

介绍 本篇Codelab使用ArkTS语言实现计步器应用&#xff0c;应用主要包括计步传感器、定位服务和后台任务功能&#xff1a; 通过订阅计步器传感器获取计步器数据&#xff0c;处理后显示。通过订阅位置服务获取位置数据&#xff0c;处理后显示。通过服务开发实现后台任务功能。…

MySQL使用教程

数据构成了我们日益数字化的社会基础。想象一下&#xff0c;从移动应用和银行系统到搜索引擎&#xff0c;再到如 ChatGPT 这样的先进人工智能聊天机器人&#xff0c;这些工具若没有数据支撑&#xff0c;将寸步难行。你有没有好奇过这些海量数据都存放在哪里呢&#xff1f;答案正…

基于JavaWeb+SpringBoot+Vue在线拍卖系统的设计和实现

基于JavaWebSpringBootVue在线拍卖系统系统的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 摘 要 1 Abstract 1 1 系统概述 4 1.1 概述 4 1.2课题意义 4 1.3 主要内容 4 2 …

Android--Jetpack--Databinding源码解析

慢品人间烟火色&#xff0c;闲观万事岁月长 一&#xff0c;基本使用 关于databinding的基本使用请看之前的文章 Android--Jetpack--Databinding详解-CSDN博客 二&#xff0c;xml布局解析 分析源码呢&#xff0c;主要就是从两方面入手&#xff0c;一个是使用&#xff0c;一个…

C语言-枚举

常量符号化 用符号而不是具体的数字来表示程序中的数字 枚举 用枚举而不是定义独立的const int变量 枚举是一种用户定义的数据类型&#xff0c;他用关键词enum以如下语法来声明&#xff1a; enum枚举类型名字{名字0&#xff0c;…&#xff0c;名字n}&#xff1b; 枚举类型名…

HubSpot细分目标市场:拓展业务边界,突破增长瓶颈

在数字化时代&#xff0c;企业面临前所未有的市场挑战。随着科技的飞速发展&#xff0c;消费者期望个性化的体验&#xff0c;即时的互动&#xff0c;以及高质量、有价值的信息。这些变化使得企业不仅需要适应新的技术和趋势&#xff0c;还需要更加精细化地理解和满足不同细分市…

【广州华锐互动】AR昆虫在线教学软件:增强现实技术带你近距离探索微观世界

随着科技的不断发展&#xff0c;教育方式也在不断创新。在这个信息爆炸的时代&#xff0c;传统的教育方式已经无法满足人们对知识的渴望。为了让孩子们更好地了解自然界的奥秘&#xff0c;一款名为“AR昆虫在线教学软件”的应用程序应运而生&#xff0c;它将带领孩子们踏上一段…

HarmonyOS应用开发-手写板

这是一个基于HarmonyOS做的一个手写板应用&#xff0c;只需要简单的几十行代码&#xff0c;就可以实现如下手写功能以及清空画布功能。 一、先上效果图&#xff1a; 二、上代码 Entry Component struct Index {//手写路径State pathCommands: string ;build() {Column() {//…

kubeadm搭建单master多node的k8s集群--小白文,图文教程

参考文献 K8S基础知识与集群搭建 kubeadm搭建单master多node的k8s集群—主要参考这个博客&#xff0c;但是有坑&#xff0c;故贴出我自己的过程&#xff0c;坑会少很多 注意&#xff1a; 集群配置是&#xff1a;一台master&#xff1a;zabbixagent-k8smaster&#xff0c;两台…

基于YOLOv7算法的高精度实时烟头目标检测识别系统(PyTorch+Pyside6+YOLOv7)

摘要&#xff1a;基于YOLOv7算法的高精度实时烟头目标检测系统可用于日常生活中检测与定位烟头目标&#xff0c;此系统可完成对输入图片、视频、文件夹以及摄像头方式的目标检测与识别&#xff0c;同时本系统还支持检测结果可视化与导出。本系统采用YOLOv7目标检测算法来训练数…

重磅!2023中国高校计算机大赛-人工智能创意赛结果出炉

目录 中国计算机大赛-人工智能创意赛现场C4-AI大赛颁奖及留影800个AI应用&#xff1f;这届大学生真能“搞事情”AI原生时代&#xff0c;百度要再培养500万大模型人才 中国计算机大赛-人工智能创意赛现场 12月8日&#xff0c;杭州&#xff0c;一位“白发老人”突然摔倒在地&…

halcon一维测量

标定的作用&#xff1a; 得到相机的内参和外参&#xff0c;即相机成像的模型规律 * fuse.hdev: measuring the width of a fuse wire * dev_update_window (off) dev_close_window () * **** * step: acquire image * **** read_image (Fuse, fuse) get_image_size (Fuse, Wid…

公交站间的距离

&#x1f388; 算法并不一定都是很难的题目&#xff0c;也有很多只是一些代码技巧&#xff0c;多进行一些算法题目的练习&#xff0c;可以帮助我们开阔解题思路&#xff0c;提升我们的逻辑思维能力&#xff0c;也可以将一些算法思维结合到业务代码的编写思考中。简而言之&#…

LeetCode 279完全平方数 139单词拆分 卡码网 56携带矿石资源(多重背包) | 代码随想录25期训练营day45

动态规划算法6 LeetCode 279 完全平方数 2023.12.11 题目链接代码随想录讲解[链接] int numSquares(int n) {//1确定dp数组&#xff0c;其下标表示j的完全平方数的最少数量//3初始化&#xff0c;将dp[0]初始化为0&#xff0c;用于计算&#xff0c;其他值设为INT_MAX用于递推…

C++联合体union

联合体 将多个类型合并到一起省空间 枚举与联合一起使用 匿名联合 类似于无作用域 &#xff23;11联合体定义非内建类型 C11 引入了能够在联合体中使用非内建类型的能力&#xff0c;这些类型包括具有自定义构造函数、析构函数、拷贝构造函数和拷贝赋值运算符的类。 关键特性…

STM32F407-14.1.0-01高级定时器简介

TIM1 和 TIM8 简介 高级控制定时器&#xff08;TIM1 和 TIM8&#xff09;包含一个 16 位自动重载计数器&#xff0c;该计数器由可编程预分频器驱动。 此类定时器可用于各种用途&#xff0c;包括测量输入信号的脉冲宽度&#xff08;输入捕获&#xff09;&#xff0c;或者生成输出…

软件运行原理 - 内存模型 - 栈内存

说明 C/C软件运行时&#xff0c;内存根据使用方式的不同分为堆内存和栈内存&#xff0c;栈内存使用有以下特征&#xff1a; 栈内存使用&#xff08;申请、释放&#xff09;由系统自动分配和释放&#xff0c;程序员不用做任何操作。栈内存重复使用&#xff0c;进入函数时数据入…

Axure安装及面板各区域详解

目录 一、Axure简介 二、Axure安装及使用准备 2.1 Axure官网 2.2 Axure授权 2.3 Axure汉化 2.4 设置RP文件保存路径 三、Axure菜单栏的使用 3.1 新建项目 3.2 新建元件库 3.3 自动备份设置 3.4 页面画布网格设置 四、Axure工具栏 4.1 选择模式 4.1.1 相交选中 4…

基于Qt的登录页面设计

题目&#xff1a; 完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和…

CyclicBarrier、CountDownLatch、Semaphore 的用法

CyclicBarrier、CountDownLatch、Semaphore 的用法 CountDownLatch&#xff08;线程计数器 &#xff09; CountDownLatch 类位于 java.util.concurrent 包下&#xff0c;利用它可以实现类似计数器的功能。比如有一个任务 A&#xff0c;它要等待其他 4 个任务执行完毕之后才能执…