【C++】深入理解 C++ 中的继承进阶:多继承、菱形继承及其解决方案

个人主页: 起名字真南的CSDN博客

个人专栏:

  • 【数据结构初阶】 📘 基础数据结构
  • 【C语言】 💻 C语言编程技巧
  • 【C++】 🚀 进阶C++
  • 【OJ题解】 📝 题解精讲

目录

  • C++继承机制详解与代码示例
  • 📌1. 继承的基本概念
  • 📌 2. 继承示例:`Student`和`Teacher`继承`Person`
  • 📌 3. 继承类模板示例
  • 📌 4. 基类和派生类间的转换
  • 📌 5. 菱形继承与虚继承
  • 📌 6. 虚继承的实现
  • 📌 7. 继承与组合的区别
  • 📌 总结

C++继承机制详解与代码示例

继承是面向对象编程(OOP)中的重要概念之一,通过继承,C++允许我们复用现有类的代码,并在此基础上进行扩展,以构建层次化的类结构。本文将结合详细的代码示例,讲解C++中的继承机制,包括单继承、多继承、菱形继承、虚继承、模板类继承等。学习这些概念将有助于我们在实际开发中设计更高效、可复用的代码结构。


📌1. 继承的基本概念

继承(inheritance)是指一个类可以获得另一个类的属性和方法,从而实现代码的复用。通过继承,派生类(也称子类)不仅能继承基类(也称父类)的成员和行为,还可以扩展或修改这些行为。C++中继承形成了“从一般到特殊”的层次结构,并且有以下几种访问控制方式:

  • public继承:基类的publicprotected成员在派生类中分别保持为publicprotected
  • protected继承:基类的public成员变为protectedprotected成员保持不变。
  • private继承:基类的publicprotected成员都变为private

示例代码展示了如何通过继承减少重复代码,提升代码复用率。


📌 2. 继承示例:StudentTeacher继承Person

在下面的示例中,我们定义了一个Person类,包含了一些基本的个人信息。然后我们通过继承创建Student类和Teacher类,分别表示学生和老师。这些类除了继承Person类的基本信息外,还包含各自特有的属性和方法。

class Person {
public:void identity() {cout << "身份认证:" << _name << endl;}
protected:string _name = "默认名字";string _address;string _tel;int _age = 18;
};class Student : public Person {
public:void study() {cout << _name << "在学习" << endl;}
protected:int _stuid;  // 学号
};class Teacher : public Person {
public:void teaching() {cout << _name << "在授课" << endl;}
protected:string _title;  // 职称
};

在该示例中:

  • Person类包含了姓名、地址、电话、年龄等个人信息。
  • Student类继承了Person类,同时增加了学号(_stuid)和study()方法,用于学生的学习行为。
  • Teacher类同样继承自Person类,增加了职称(_title)和teaching()方法,用于表示老师的授课行为。

这种设计避免了在StudentTeacher中重复定义姓名、年龄等成员变量,使代码更加简洁。


📌 3. 继承类模板示例

C++支持模板类继承,通过继承标准库的容器类,我们可以轻松地扩展这些容器类的功能。以下示例展示了一个栈(Stack)类模板,分别继承自std::vectorstd::liststd::deque,从而实现栈的基本操作。

template<class T>
class Stack : public std::vector<T> {
public:void push(const T& x) {std::vector<T>::push_back(x);}void pop() {std::vector<T>::pop_back();}bool empty() {return std::vector<T>::empty();}
};

在这个模板类示例中,Stack类继承了std::vector模板类,并添加了push()pop()empty()方法:

  • push:在栈顶添加一个元素;
  • pop:移除栈顶的元素;
  • empty:判断栈是否为空。

模板类在继承时需要特别注意类域的使用。在上例中,vector<T>::push_back(x)明确指出调用vector模板类的成员函数push_back,以确保模板实例化时找到正确的成员函数。这种方式提供了简便的接口,使得Stack类直接具备了向量的功能而无需重写。


📌 4. 基类和派生类间的转换

在C++中,基类指针或引用可以指向派生类对象,从而通过基类指针或引用调用派生类对象的基类成员,这种机制称为“切片”或“切割”。以下代码示例展示了这种转换的用法:

Student sobj;
Person* pp = &sobj;       // 基类指针指向派生类对象
Person& rp = sobj;        // 基类引用指向派生类对象

这种转换称为“向上转换”(upcasting),因为派生类对象可以转换为基类类型。这在多态性设计中非常有用,可以使代码更加通用。相反,基类对象不能直接转换为派生类对象,但可以通过强制类型转换来实现。这种转换称为“向下转换”(downcasting),需要开发者确保安全性,可以使用dynamic_cast来进行运行时类型检查。

注意:向下转换必须确认基类指针实际指向派生类对象,否则会引发运行时错误。


📌 5. 菱形继承与虚继承

C++支持多继承,即一个类可以继承自多个基类。然而,如果多个基类继承自相同的祖先类,就会导致菱形继承问题。如下所示,Assistant类继承了TeacherStudent,而这两个类都继承自Person

class Person {
public:string _name = "莱昂纳多";int _num = 111;
};class Student : virtual public Person {};
class Teacher : virtual public Person {};class Assistant : public Teacher, public Student {
protected:string _major;
};

此时,Assistant类将拥有两份Person的成员变量,导致数据冗余。此外,访问_name等成员变量时会引起二义性问题。通过虚继承可以避免这一问题。


📌 6. 虚继承的实现

虚继承(virtual inheritance)是解决菱形继承问题的一种方法。通过虚继承,派生类可以确保祖先类的成员只有一份,从而消除了菱形继承中的数据冗余和访问二义性问题。以下代码展示了虚继承的用法:

class Person {
public:string _name = "莱昂纳多";int _num = 111;
};class Student : virtual public Person {};
class Teacher : virtual public Person {};class Assistant : public Teacher, public Student {
protected:string _major;
};int main() {Assistant a;a._name = "小李";  // 仅有一个 _name,避免二义性cout << "Name: " << a._name << endl;return 0;
}

通过在StudentTeacher类中使用virtual public继承Person,虚继承确保了Assistant类仅保留一份Person的成员变量,这样既解决了数据冗余问题,又避免了访问时的二义性。


📌 7. 继承与组合的区别

在面向对象设计中,继承组合是两个不同的设计思路:

  • 继承(is-a关系):用于表示派生类是基类的一种特殊类型。继承常用于表示类之间的层次结构,例如Car类和BMW类。
  • 组合(has-a关系):用于表示类之间的包含关系。组合常用于表示类之间的拥有关系,例如Car类包含多个Tire对象(轮胎)。

组合优于继承,通常能降低类与类之间的耦合性,使代码更加灵活。仅当派生类确实是基类的一种“特殊类型”时,才考虑使用继承。

例如,在以下代码中,Tire类表示轮胎,Car类组合了多个Tire对象,因为车和轮胎是拥有关系,而不是层次关系。

class Tire {
protected:string _brand = "Michelin";  // 轮胎品牌
};class Car {
protected:string _color = "白色";  // 车颜色Tire _tire1, _tire2, _tire3, _tire4;  // 4个轮胎
};

在该示例中,CarTire是组合关系,Car对象拥有四个`Tire

对象,说明两者间的关系是has-a`,更适合组合关系,而非继承关系。


📌 总结

C++的继承机制提供了代码复用和层次结构的基础,但其灵活性也带来了复杂性。本文介绍了C++继承的多种使用方式和注意事项,如模板类的继承、基类与派生类的转换、菱形继承和虚继承的使用等。菱形继承可能导致数据冗余和访问冲突,虚继承可以解决这一问题,但会增加实现的复杂性。因此,在设计中要谨慎使用继承,尽量优先选择组合关系,以降低耦合性,提高代码的可维护性和复用性。


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

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

相关文章

【MATLAB源码-第218期】基于matlab的北方苍鹰优化算法(NGO)无人机三维路径规划,输出做短路径图和适应度曲线.

操作环境&#xff1a; MATLAB 2022a 1、算法描述 北方苍鹰优化算法&#xff08;Northern Goshawk Optimization&#xff0c;简称NGO&#xff09;是一种新兴的智能优化算法&#xff0c;灵感来源于北方苍鹰的捕猎行为。北方苍鹰是一种敏捷且高效的猛禽&#xff0c;广泛分布于北…

C#中的二维数组的应用:探索物理含义与数据结构的奇妙融合

在C#编程中&#xff0c;二维数组&#xff08;或矩阵&#xff09;是一种重要的数据结构&#xff0c;它不仅能够高效地存储和组织数据&#xff0c;还能通过其行、列和交叉点&#xff08;备注&#xff1a;此处相交处通常称为“元素”或“单元格”&#xff0c;代表二维数组中的一个…

利用uniapp开发鸿蒙:运行到鸿蒙模拟器—踩坑合集

从uniapp运行到鸿蒙模拟器上这一步&#xff0c;就有非常多的坑&#xff0c;一些常见的坑&#xff0c;官网都有介绍&#xff0c;就不再拿出来了&#xff0c;这里记录一下官网未记录的大坑 1.运行路径从hbuilderx启动鸿蒙模拟器 解决方法&#xff1a; Windows系统&#xff0c;官…

跨平台WPF框架Avalonia教程 十三

AutoCompleteBox 自动补全输入框 自动补全输入框提供了一个供用户输入的文本框和一个包含可能匹配项的下拉列表。下拉列表会在用户开始输入时显示&#xff0c;并且每输入一个字符&#xff0c;匹配项都会更新。用户可以从下拉列表中选择匹配项。 文本与可能项匹配的方式是可配…

开发中使用UML的流程_02 CIM-1:定义业务流程

CIM-1定义业务流程&#xff08;业务用例模型&#xff09;的生成&#xff0c;有下列两项&#xff1a; 1.业务用例图 2.业务用例简述 业务用例图的主要组成元素是业务用例和业务执行者。 图中的一个业务用例代表一条业务流程&#xff0c;业务执行者则代表位于业务组织外但会启动…

Streamlit + AI大模型API实现视频字幕提取

简介 在本文中&#xff0c;我将带你探讨如何使用Streamlit和AI大模型API来实现视频字幕提取的技术。Streamlit是一个开源的Python库&#xff0c;用于快速构建数据应用的Web界面&#xff0c;而AI大模型API&#xff0c;如OpenAI&#xff0c;提供了强大的语言处理能力&#xff0c…

c++--------《set 和 map》

c--------《set 和 map》 1 set系列的使⽤1.1 set类的介绍1.2 set的构造和迭代器1.3 set重要接口 2 实现样例2.1: insert和迭代器遍历使⽤样例&#xff1a;2.2: find和erase使⽤样例&#xff1a; 练习3.map系列的使用3.1 map类的介绍3.1.1 pair类型介绍 3.2 map的数据修改3.3mu…

计算机网络——路由选择算法

路由算法 路由的计算都是以子网为单位计算的——找到从原子网到目标子网的路径 链路状态算法 序号——&#xff08;源路由器&#xff0c;序号&#xff09;——如果发现这个序号重复或者老了——就不扩散 先测量——再泛洪获得路由 路由转发情况 若S——>W是21则不更改——…

同三维T80004EHU 高清HDMI/USB编码器

同三维T80004EHU 高清HDMI/USB编码器 1路HDMI或1路USB输入&#xff0c;带1路3.5音频输入&#xff0c;高清1080P60 同三维T80004EHU 高清HDMI/USB编码器 产品简介&#xff1a; 同三维T80004EHU高清HDMI/USB编码器是一款1路HDMI或1路USB高清编码器。可将 HDMI 或USB视频源编码…

RGB与YCbCr转换算法

目录 RGB与YCbCr转换算法RGB与YCbCr色域介绍RGB模型YCbCr色域简介YCbCr的应用YUV 和 YCbCr 的区别 色彩转换公式 RGB 转 YCbCr 实现RGB 转 YCbCr 的 Matlab 实现RGB 转 YCbCr 的 FPGA 实现 YCbCr 转 RGB 实现YCbCr 转 RGB 的 Matlab 实现YCbCr 转 RGB 的 FPGA 实现 RGB与YCbCr转…

子串【Lecode_HOT100】

1.和为K的子数组No.560 前缀和枚举 public int subarraySum(int[] nums, int k) {int count 0;//满足条件的个数//计算前缀和int[] preSum new int[nums.length1];for(int i 1 ; i<preSum.length;i){preSum[i]preSum[i-1]nums[i-1];}//查找满足kfor(int l 0;l<preSum…

13.C++内存管理2(C++ new和delete的使用和原理详解,内存泄漏问题)

⭐本篇重点&#xff1a;new, delete的使用和原理 ⭐本篇代码&#xff1a;c学习/04.c-动态内存管理 橘子真甜/c-learning-of-yzc - 码云 - 开源中国 (gitee.com) 目录 一. new和delete的使用 1.1 操作内置类型 1.2 操作自定义类型 二. new, delete与malloc, free的区别 2.1…

vue中动态渲染静态图片资源

不报错且f12查看元素的时候&#xff0c;显示的src说明已经渲染到html的src上&#xff0c;但是就是不显示在页面上 原因 在vue上&#xff0c;动态渲染静态图片资源&#xff08;比如从assets文件夹加载的图片&#xff09;需要注意打包工具对静态资源的解析方式 由于vue2的脚手…

uniapp 相关的swiper的一些注意事项

先推荐一个一个对标pc端swiper的uniapp版本 zebra-swiper 缺点是自定义分页器不是很好处理 不知道怎么弄 优点:可以进行高度自适应 &#xff08;这个uniapp原生swiper没有 只能动态修改 采用js 或者只有几种固定高度时采用变量修改&#xff09; <swiperref"lifeMiddle…

豆瓣书摘 | 爬虫 | Python

获取豆瓣书摘&#xff0c;存入MongoDB中。 import logging import timeimport requests from bs4 import BeautifulSoup from pymongo import MongoClientheaders {accept: text/html,application/xhtmlxml,application/xml;q0.9,image/avif,image/webp,image/apng,*/*;q0.8,…

(Linux)搭建静态网站——基于http/https协议的静态网站

简单了解nginx配置文件 1.下载并开启nginx服务 下载 [rootlocalhost ~]# dnf install nginx -y开启 [rootlocalhost ~]# systemctl restart nginx 1.(1)搭建静态网站——基于http协议的静态网站 实验1&#xff1a;搭建一个web服务器&#xff0c;访问该服务器时显示“hello w…

含有非期望产出的EBM模型及其改进模型

含有非期望产出的EBM模型及其改进模型 今天推出的是含有非期望产出的EBM模型及其两种改进模型。 **参考文献&#xff1a;《基于数字经济要素组合的绿色全要素生产率提升研究中的模型》**杜娟&#xff0c;张子承&#xff0c;王熠 本文构建了考虑非期望产出的改进EBM&#xff…

VScode学习前端-01

小问题合集&#xff1a; vscode按&#xff01;有时候没反应&#xff0c;有时候出来&#xff0c;是因为------>必须在英文状态下输入&#xff01; 把鼠标放在函数、变量等上面&#xff0c;会自动弹出提示&#xff0c;但挡住视线&#xff0c;有点不习惯。 打开file->pre…

使用 .NET 创建新的 WPF 应用

本教程介绍如何使用 Visual Studio 创建新的 Windows Presentation Foundation &#xff08;WPF&#xff09; 应用。 使用 Visual Studio&#xff0c;可以向窗口添加控件以设计应用的 UI&#xff0c;并处理这些控件中的输入事件以与用户交互。 在本教程结束时&#xff0c;你有一…

自存 sql常见语句和实际应用

关于连表 查询两个表 SELECT * FROM study_article JOIN study_article_review 查询的就是两个表相乘&#xff0c;结果为两个表的笛卡尔积 相这样 这种并不是我们想要的结果 通常会添加一些查询条件 SELECT * FROM study_articleJOIN study_article_review ON study_art…