深入解析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|  00      | &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.mzph.cn/news/214948.shtml

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

相关文章

BigData之Google Hadoop中间件安装

前言 Hadoop / Zookeeper / Hbase 因资源有限 这三个都是安装在同一台Centos7.9的机器上 但通过配置 所以在逻辑上是distributed模式 1 Java安装 1.1 下载java11 tar/opt/java/jdk-11.0.5/ 1.2 环境配置修改 文件/etc/profile export JAVA_HOME/opt/java/jdk-11.0.5/ e…

HarmonyOS编译开源native库(OpenSSL实例)

前言 近期项目要开始做鸿蒙版本&#xff0c;有一部分依赖native的代码也需要迁移&#xff0c;某个native模块依赖openssl&#xff0c;需要在鸿蒙下重新编译openssl才行。一开始找了很多相关文档都没有得到方法&#xff0c;无奈只能自己凭经验慢慢试&#xff0c;最后还是成功了…

JS基础之执行上下文

JS基础之执行上下文 执行上下文顺序执行可执行代码执行上下文栈回顾上文 执行上下文 顺序执行 写个JavaScript的开发者都会有个直观的印象&#xff0c;那就是顺序执行&#xff1a; var foo function(){console.log(foo1) } foo(); //foo1 var foo function(){console.log(…

HTML面试题---专题一

文章目录 一、前言二、 HTML5 中 <header> 和 <footer> 标签的用途是什么&#xff1f;三、如何在 HTML 中嵌入 SVG&#xff08;可缩放矢量图形&#xff09;文件&#xff1f;四、解释 contenteditable 属性的用途五、如何创建随屏幕尺寸缩放的响应式图像&#xff1f…

线上扭蛋机小程序搭建,扭蛋与科技的完美结合

扭蛋机作为当下比较热门的一种盲盒玩法&#xff0c;在年轻人群体中非常受欢迎。随着经济的增长和人们生活水平的提高&#xff0c;人们对娱乐消费需求也在增加&#xff0c;扭蛋机的受众群体也在扩大。 目前线上扭蛋机小程序也获得了大众的青睐&#xff0c;扭蛋机小程序就是把线…

记录一下快速上手Springboot登录注册项目

本教程需要安装以下工具&#xff0c;如果不清楚怎么安装的可以看下我的这篇文章 链接: https://blog.csdn.net/qq_30627241/article/details/134804675 管理工具&#xff1a; maven IDE&#xff1a; IDEA 数据库&#xff1a; MySQL 测试工具&#xff1a; Postman 打开IDE…

AR-LDM原理及代码分析

AR-LDM原理AR-LDM代码分析pytorch_lightning(pl)的hook流程main.py 具体分析TrainSampleLightningDatasetARLDM blip mm encoder AR-LDM原理 左边是模仿了自回归地从1, 2, ..., j-1来构造 j 时刻的 frame 的过程。 在普通Stable Diffusion的基础上&#xff0c;使用了1, 2, .…

网工内推 | 项目经理专场,最高20K*13薪,软考证书优先

01 Trasen 招聘岗位&#xff1a;大项目经理&#xff08;医疗行业/HIS&#xff09; 职责描述&#xff1a; 1.负责项目按计划完成交付并顺利验收结项&#xff1b; 2.参与项目前期预算、评审、方案设计等&#xff1b; 3.负责具体项目实施&#xff0c;制定项目计划、组织项目资源、…

Web网站服务(二)

1、客户机地址限制。 Require all granted&#xff1a;表示允许所有主机访问。 Require all denied&#xff1a;表示拒绝所有主机访问。 Require local&#xff1a;表示仅允许本地主机访问。 Require [not] host <主机名或域名列表>&#xff1a;表示允许或拒绝指定主机或…

Web安全-SQL注入【sqli靶场第11-14关】(三)

★★实战前置声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将其信息做其他用途&#xff0c;由用户承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 0、总体思路 先确认是否可以SQL注入&#xff0…

轻空间助力中国高校实现场馆多功能一体化

中国高校在迎接体育、文艺、学术等多元化需求的同时&#xff0c;面临着场馆设施不足、建设成本高的难题。然而&#xff0c;随着轻空间&#xff08;江苏&#xff09;膜科技有限公司的崭新解决方案的引入&#xff0c;中国高校如苏州大学等正迎来一场场馆多功能一体化的革命。 创新…

电源小白入门学习4——LDO的选择与使用技巧

电源小白入门学习4——LDO的选择与使用技巧 LDO简介LDO工作原理LDO选型LDO并联问题LDO的新发展 上期我们介绍了开关电源系统中一些常见的元器件&#xff0c;这期我们来学习LDO的选择与使用技巧 LDO简介 LDO的全称是低压差线性稳压器&#xff08;Low Drop-Out Linear Regulator…

计算机病毒判定专家系统原理与设计《文字提取人工修正》

内容源于网络。网络上流转的版本实在是不易阅读&#xff0c; 又不忍神作被糟蹋故稍作整理&#xff0c;对于内容仍然有识别不准的地方&#xff0c;网友可留言&#xff0c;我跟进修改。 雷 军 &#xff08;武汉大学计算机系&#xff0c;430072) 摘要: 本文详细地描述了…

【UML】NO.2 UML必须了解的基础知识(举例)

目录 一、UML的构成 1.1 事物 1.2 关系 1.3 图 二、事物 2.1 结构事物 2.1.1 类&#xff08;class&#xff09; 2.1.2 接口 2.1.3 协作 2.1.4 用例 2.1.5 主动类 2.1.6 构件 2.1.7 节点 2.2 行为事物 2.2.1 交互 2.2.2 状态机 2.2.3 活动 2.3 分组事物 包 …

Unittest单元测试框架

Unittest介绍、单元测试用例的组织、测试用例的执行、测试用例的跳过 Unittest介绍 为什么要学习单元测试框架 测试用例的组织与运行需要单元测试框架的参与&#xff0c;从而满足不同测试场景的需要&#xff0c;单元测试框架提供了丰富的比较方法&#xff1a;实际结果与预期结…

Viewport Meta 标记:让网页适应各种设备的魔法符号

在我们用手机或平板电脑浏览网页时&#xff0c;你是否曾发现有些网页能够很好地适应屏幕&#xff0c;而有些却需要左右滑动才能完整显示内容&#xff1f;这就涉及到一个神奇的东西——Viewport Meta 标记。 最近本人在研究自适应的各自实现方法&#xff0c;比如media媒体查询、…

6个免费设计素材库,设计师都在用,赶紧收藏!

设计师应该都知道&#xff0c;在设计过程中找素材真的很费时间&#xff0c;有的时候全网翻遍都未必能找到自己想要的&#xff0c;以至于现在很多设计师都花钱去购买素材&#xff0c;你说要是拿去参赛或者商用还好&#xff0c;就拿平常设计来说你舍得花这个钱去买吗&#xff0c;…

亚马逊云科技Amazon Bedrock,现推出更多模型选择和全新强大功能

亚马逊云科技在re:Invent 2023上宣布推出Amazon Bedrock更多模型选择和强大功能&#xff0c;帮助客户更轻松地构建和规模化针对其业务定制的生成式AI应用程序。 Amazon Bedrock是一项全面托管的服务&#xff0c;用户可轻松访问来自AI21 Labs、Anthropic、Cohere、Meta、Stabili…

网页设计的灵感从哪来?试试这15个灵感网站

设计灵感网站是许多设计师必备的工具&#xff0c;因为它们提供了一个创造性的源泉&#xff0c;可以帮助设计师找到灵感和灵感&#xff0c;从而开发出惊人的设计。 推荐15个设计灵感网站&#xff0c;涵盖了平面设计、网页设计、UI设计等不同领域的设计。 即时设计资源广场 即…