C++基础面试题 | 什么是C++中的虚继承?

news2024/9/28 3:26:41

在这里插入图片描述

文章目录

    • 回答重点
      • 菱形继承问题
      • 虚继承解决菱形继承问题
      • 虚继承的二义性解决
    • 虚继承总结
    • 拓展知识:virtual关键字的用法
      • 1. 虚函数 (Virtual Function)
      • 2. 纯虚函数 (Pure Virtual Function)
      • 3. 虚析构函数 (Virtual Destructor)
      • 4. 虚继承 (Virtual Inheritance)
      • 5. 虚函数表 (Vtable) 和 虚函数指针 (Vptr)

回答重点

虚继承是C++中的一种继承方式,用于解决菱形继承(也称为钻石继承)问题。虚继承可以确保从一个共同基类派生的多个子类只会在最派生类中继承一份该基类的成员,而不会产生多个冗余基类,从而避免冗余和二义性。

菱形继承问题

假设有以下继承结构:

    A
   / \
  B   C
   \ /
    D

在这个继承关系中,类 B 和类 C 都继承自类 A,然后类 D 同时继承自 BC。这就形成了一个菱形结构。

非虚继承的情况下,D 会有两份 A 的成员:一份来自 B,一份来自 C。这不仅浪费内存,还会引发二义性问题。例如,D 对象调用 A 类中的成员时,编译器无法确定该成员来自 B 继承的 A,还是来自 C 继承的 A

虚继承解决菱形继承问题

为了避免这个问题,C++引入了虚继承。通过虚继承,基类 A 在子类 BC 中只会有一份副本,而不是每个派生类保留一份副本。

语法
虚继承通过在继承时加上 virtual 关键字实现:

class A {
public:
    int value;
};

class B : virtual public A {
    // 虚继承A
};

class C : virtual public A {
    // 虚继承A
};

class D : public B, public C {
    // D 从B和C继承,A只存在一份
};

BC 都通过虚继承方式继承自 A,因此当 D 继承自 BC 时,A 的成员在 D 中只存在一份。通过将中间的继承对象声明为虚继承,避免了基类冗余和二义性。

虚继承的二义性解决

即使通过虚继承解决了成员变量的二义性,构造函数的调用仍需要在派生类中明确指定。例如:

class D : public B, public C {
public:
    D() : A(), B(), C() {}  // 需要显式调用A的构造函数
};

虚继承总结

  • 虚继承用于解决菱形继承问题。
  • 在虚继承中,基类的成员只会有一份副本,从而避免冗余和二义性问题。
  • 虚继承虽然解决了复杂继承结构中的问题,但引入了一定的复杂性,需要在构造函数中显式调用基类的构造函数。

拓展知识:virtual关键字的用法

virtual 关键字在C++中主要用于以下四个场景:

  1. 虚函数:实现多态。
  2. 纯虚函数:定义抽象接口。
  3. 虚析构函数:保证正确析构派生类对象。
  4. 虚继承:解决菱形继承中的二义性问题。

1. 虚函数 (Virtual Function)

虚函数用于实现运行时多态。当基类的函数被声明为虚函数时,派生类可以重写这个函数,并在通过基类指针或引用调用时,动态地选择调用派生类的函数版本。

语法:

class Base {
public:
    virtual void display() { 
        std::cout << "Base display" << std::endl; 
    }
};

class Derived : public Base {
public:
    void display() override {  // override 表示派生类重写基类虚函数
        std::cout << "Derived display" << std::endl;
    }
};

int main() {
    Base* obj = new Derived();
    obj->display();  // 调用的是Derived类的display函数
}

关键点:

  • 虚函数允许在运行时根据对象类型来决定调用哪个版本的函数。
  • 派生类的函数如果与基类的虚函数同名并且参数相同,则会重写该虚函数。
  • 可以使用 override 关键字显式声明重写,但这不是强制的。

2. 纯虚函数 (Pure Virtual Function)

纯虚函数是一种特殊的虚函数,用于在基类中定义接口而不提供实现,要求所有派生类必须实现该函数。包含纯虚函数的类称为抽象类,不能直接实例化。

语法:

class Base {
public:
    virtual void display() = 0;  // 纯虚函数
};

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

关键点:

  • 包含纯虚函数的类不能被实例化,必须由派生类实现纯虚函数。
  • 纯虚函数用于定义抽象接口,强制派生类实现特定功能。

3. 虚析构函数 (Virtual Destructor)

虚析构函数用于确保通过基类指针删除派生类对象时,能够正确调用派生类的析构函数,防止内存泄漏。

语法:

class Base {
public:
    virtual ~Base() { 
        std::cout << "Base Destructor" << std::endl; 
    }
};

class Derived : public Base {
public:
    ~Derived() { 
        std::cout << "Derived Destructor" << std::endl; 
    }
};

int main() {
    Base* obj = new Derived();
    delete obj;  // 调用Derived的析构函数,然后调用Base的析构函数
}

关键点:

  • 如果基类没有虚析构函数,通过基类指针删除派生类对象时,只会调用基类的析构函数,导致派生类的资源未释放。
  • 使用虚析构函数保证了正确的析构顺序:先调用派生类的析构函数,再调用基类的析构函数。

4. 虚继承 (Virtual Inheritance)

虚继承用于解决菱形继承问题,确保从多个派生类继承自同一个基类时,基类只被继承一份,避免重复继承和二义性。

语法:

class A {
public:
    int value;
};

class B : virtual public A { };  // 虚继承
class C : virtual public A { };  // 虚继承

class D : public B, public C { };  // D中只会有一份A

关键点:

  • 虚继承可以避免菱形继承中多次继承基类的问题。
  • 基类在派生类中只存在一份,减少冗余和二义性。

5. 虚函数表 (Vtable) 和 虚函数指针 (Vptr)

虽然这是 virtual 关键字的底层实现机制,但了解虚函数表和虚函数指针有助于理解 virtual 的运行机制。

  • 虚函数表 (Vtable):类包含虚函数时,编译器会生成一个虚函数表,其中记录了类的虚函数地址。
  • 虚函数指针 (Vptr):每个对象都包含一个指向虚函数表的指针,调用虚函数时,通过该指针找到正确的函数实现。

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

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

相关文章

一篇文章带你入门机器学习 Part1 -->Machine Learning from Scratch

学习网站&#xff1a;Machine Learning from Scratch Machine Learning from Scratch (Part1神经网络&#xff09; 神经网络——Neural Networks神经网络是如何工作的&#xff1f;训练神经网络 神经网络——Neural Networks 在人工神经网络的背景下&#xff1a;一个神经元是一…

046全排列

题意 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 提示&#xff1a; 1 < nums.length < 6 -10 < nums[i] < 10 nums 中的所有整数 互不相同 难度 中等 示例 示例 1&#xff1a; 输入&#xff1…

uniapp+若依 开发租房小程序源码分享

1、使用Uniapp开发的前台&#xff0c;基于 Vue.js 开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到iOS、Android、Web&#xff08;响应式&#xff09;、以及各种小程序 2、基于SpringBoot的权限管理系统&#xff0c;易读易懂、界面简洁美观。 核心…

WordBN字远笔记!更新1.2.2版本|Markdown编辑器新增高亮功能,界面新增深色模式

WordBN字远笔记1.2.2版本更新描述 WordBN字远笔记在1.2.2版本中进行了多项重要的更新与改进&#xff0c;旨在提升用户的编辑体验和视觉舒适度。 以下是本次更新的两大亮点&#xff1a;Markdown编辑器新增高亮功能以及界面新增深色模式。 1. Markdown编辑器新增高亮功能 在1…

零倾覆力矩点(ZMP)

系列文章目录 前言 在机器人学中&#xff0c;零倾力矩点&#xff08;ZMP&#xff09;是一个特征点&#xff0c;主要用于足式运动。在下文的一些假设中&#xff0c;我们将看到&#xff0c;它非正式地代表了一个系统接触反作用力的结果点。例如&#xff0c;下图中的刚体处于静态平…

leetcode:布尔运算(动态规划版)

最近又要考试&#xff0c;勉励自己复习一些之前学过的&#xff01;&#xff01;&#xff01; 开始使用的是DFS&#xff0c;遍历所有可能的情况&#xff0c;发现超时&#xff01; 下面的是动态规划的一个模板&#xff0c;dp[i][j][result]表示从s的第i个元素到第个元素&#xf…

Auracast认证:蓝牙广播音频的革新之旅

低功耗音频&#xff08;LE Audio&#xff09;技术的突破&#xff0c;为蓝牙世界带来了前所未有的广播音频功能。Auracast™&#xff0c;作为蓝牙技术联盟精心打造的音频广播解决方案&#xff0c;正引领着一场全新的音频分享革命。它不仅革新了传统蓝牙技术的局限&#xff0c;更…

HuggingFace Embedding 转为 Ollama Embedding

Ollama 是基于 LlamaCpp 开发的 CPU 上的推理引擎&#xff0c;通过 LlamaCpp 提供的脚本可以将大语言模型装换为 gguf 的二进制跟是文件&#xff0c;从而通过 Ollama 就行推理。Ollama 支持HuggingFace 大多开源模型&#xff0c;例如 Llama、Qwen、Gemma 和 Phi3 等等。 GGUF …

【Leetcode:2848. 与车相交的点 + 模拟计数】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

软件研制功能点拆分

最近需要进行软件研制概算明细表中的估算对象原始功能点&#xff0c;记录一下学习过程&#xff0c;共有EI(external input 外部输入)、EO(外部输出)、EQ(外部查询)、ILF(internal logic 内部逻辑文件)、EIF(外部接口文件)五个。 功能点计数项分为数据功能&#xff08;逻辑文件&…

【数据仓库】数据仓库常见的数据模型——范式模型

目录 一、范式 1、第一范式 2、第二范式 3、第三范式 4、进一步范式化&#xff1a;BCNF、4NF 和 5NF 简介 &#xff08;1&#xff09;Boyce-Codd 范式&#xff08;BCNF&#xff09; &#xff08;2&#xff09;第四范式&#xff08;4NF&#xff09; &#xff08;5&#x…

光华里社区“电亮生活”行动:智能科技携手志愿温情,老旧小区焕发新生机

在朝阳区建外街道光华里社区&#xff0c;一场关于“电”的革命正悄然改变着居民的生活面貌。面对老旧小区普遍存在的电力设施陈旧、线路老化、电压不稳等老大难问题&#xff0c;社区党委没有坐视不管&#xff0c;而是携手北京中兴物业管理有限公司广联物业管理中心党支部&#…

泽众P-One性能测试平台支持分布式全链路压测

在当今数字化转型加速的时代&#xff0c;高性能、高可用性的系统已成为企业竞争力的核心要素之一。为了确保系统能够在高并发、大数据量的环境下稳定运行&#xff0c;分布式全链路压测成为了不可或缺的一环。P-One凭借其强大的功能&#xff0c;支持分布式全链路压测&#xff0c…

什么是 SMB 服务器以及它如何工作?

在本文中&#xff0c;您将了解 SMB 服务器以及它们如何促进网络文件共享。 我们将介绍它们的基本功能、主要特性以及如何安全地设置它们。无论您是新手还是需要复习&#xff0c;本指南都将帮助您更好地了解 SMB 服务器。 什么是 SMB 服务器&#xff1f; SMB&#xff08;服务器…

day19JS-AJAX数据通信

1. 什么是AJAX 原生生js中有两种通信&#xff0c;一个ajax&#xff0c;还有一个是fetch。 AJAX 并不是编程语言&#xff0c;是一种从网页访问 Web 服务器的技术。AJAX 代表异步 JavaScript 和 XML。 AJAX 使用浏览器内建的 XMLHttpRequest 对象从 web 服务器请求数据&#xff0…

【开放词汇检测】MM-Grounding-DINO论文翻译

摘要 Grounding-DINO 是一种先进的开放式检测模型&#xff0c;能够处理包括开放词汇检测&#xff08;Open-Vocabulary Detection&#xff0c;OVD&#xff09;、短语定位&#xff08;Phrase Grounding&#xff0c;PG&#xff09;和指代表达理解&#xff08;Referring Expressio…

Java多线程——模拟接力赛跑

题目&#xff1a; 多人参加1000米接力跑 每人跑100米&#xff0c;换下个选手 每跑10米显示信息 解题思路&#xff1a; 1.必须要用到多线程的锁&#xff0c;否则就会出现三个选手乱跑的情况&#xff0c;我们需要一个一个跑 2.使用给oneRunner上锁的方式更细的控制资源比直接给…

qt画板v1.0

qt图形视图做的一个工具&#xff0c;具备画板功能&#xff0c;对初学习有很大作用

搭建内网文件服务器(FTP),以及实现内网Gitee

一、实现windows搭建FTP&#xff0c;实现文件共享和管理 具体步骤&#xff1a; 1.打开控制面板&#xff0c;搜索功能 2.打开这几个配置 3.打开IIS&#xff0c;添加FTP站点即可 二、实现内网Gitee 参考博客&#xff1a; Gitblit服务器搭建及Git使用-CSDN博客 jdk1.8.0的安…

关系数据库(1,2)

目录 关系 域 笛卡尔集 元组 分量 基数 码 关系模式 关系模式的表示方式 关系数据库 基本关系操作 完整性 关系 单一的数据结构&#xff0c;二维表是一个逻辑结构&#xff0c;关系模型建立在集合代数的基础上。 域 指具有相同数据类型的集合。 笛卡尔集 笛卡尔集是…