C++进阶-1-单继承、多继承、虚继承

C++单继承详解

1. 基础概念

继承是面向对象编程中的一个核心概念,允许一个类(子类或派生类)继承另一个类(父类或基类)的属性和方法。单继承意味着一个类只能直接继承一个父类。这种简单的结构在许多情况下是足够的,特别是在类层次结构较为简单的情况下。

2. 语法结构

在C++中,单继承的基本语法结构如下:

cpp

class Base {// 基类内容
};class Derived : public Base {// 派生类内容
};
  • Base:基类,包含公共或保护成员。
  • Derived:派生类,继承自基类,可以访问基类的公共和保护成员。

3. 访问权限

C++提供了三种访问权限控制:

  • public:公共成员可以被任何地方访问,包括通过派生类访问。
  • protected:保护成员只能在基类及其派生类中访问,外部代码无法访问。
  • private:私有成员只能在基类内部访问,派生类无法直接访问。

示例代码

以下是一个详细的示例,展示了单继承的基本特性和用法。

cpp

#include <iostream>
using namespace std;// 基类
class Animal {
protected:string name;public:Animal(string n) : name(n) {} // 构造函数void speak() {cout << name << " says: Hello!" << endl;}
};// 派生类
class Dog : public Animal {
public:Dog(string n) : Animal(n) {} // 调用基类构造函数void bark() {cout << name << " barks!" << endl;}
};int main() {Dog myDog("Buddy");myDog.speak(); // 调用基类方法myDog.bark();  // 调用派生类方法return 0;
}

4. 构造与析构

在单继承中,子类的构造函数会调用父类的构造函数。可以使用初始化列表来实现这一点。析构函数也遵循相同的规则,派生类的析构函数会在对象销毁时自动调用基类的析构函数。

cpp

class Base {
public:Base() { cout << "Base Constructor" << endl; }~Base() { cout << "Base Destructor" << endl; }
};class Derived : public Base {
public:Derived() { cout << "Derived Constructor" << endl; }~Derived() { cout << "Derived Destructor" << endl; }
};int main() {Derived d; // 输出基类和派生类构造函数return 0;  // 输出派生类和基类析构函数
}

5. 多态性

单继承也可以实现多态性。通过基类指针或引用,可以调用派生类的重载方法。

cpp

class Base {
public:virtual void show() {cout << "Base class" << endl;}
};class Derived : public Base {
public:void show() override {cout << "Derived class" << endl;}
};void display(Base* b) {b->show(); // 调用时根据对象类型决定调用哪个方法
}int main() {Base b;Derived d;display(&b); // 输出 "Base class"display(&d); // 输出 "Derived class"return 0;
}

6. 优缺点

优点

  1. 简单性:单继承结构清晰,易于理解和使用。
  2. 易于维护:代码结构简单,便于维护和扩展。
  3. 减少复杂性:避免了多重继承带来的复杂性和潜在问题,如二义性。

缺点

  1. 灵活性不足:单继承限制了类的扩展性,无法利用多重继承的灵活性。
  2. 代码重复:如果多个类需要共享相同的功能,可能需要在多个类中重复代码。

7. 单继承的设计模式

单继承在许多设计模式中被广泛应用。例如:

  • 模板方法模式:基类定义一个算法的框架,子类可以实现具体的步骤。
  • 策略模式:基类定义一个接口,子类实现不同的策略。

8. 实际应用

在实际应用中,单继承被广泛用于类的层次结构设计。例如,在图形界面编程中,可以有一个基本的窗口类,所有特定类型的窗口(如对话框、主窗口等)都可以继承自这个基本窗口类。

9. 总结

C++中的单继承是一种重要的面向对象编程特性,使得代码的组织和结构变得更加清晰。在设计类结构时,应根据项目需求和复杂性,合理选择单继承或多重继承。尽管单继承有其局限性,但在许多情况下,它仍然是实现类之间关系的有效方式。

C++多继承详解

1. 基础概念

多继承是面向对象编程中的一个特性,允许一个类同时继承多个类的属性和方法。在C++中,类可以通过多继承来组合多个基类的功能,形成更复杂的类层次结构。这种灵活性使得代码复用变得更加高效,但也引入了一些复杂性。

2. 语法结构

在C++中,使用逗号分隔多个父类来实现多继承,基本语法如下:

cpp

class Base1 {// 基类1的内容
};class Base2 {// 基类2的内容
};class Derived : public Base1, public Base2 {// 派生类的内容
};

3. 访问权限

在多继承中,访问权限可以对每个基类单独设置。例如,派生类可以使用 publicprotectedprivate 来继承多个基类:

cpp

class Derived : public Base1, protected Base2 {// 访问权限根据需要设置
};

示例代码

以下是一个多继承的示例,展示了如何从多个基类继承功能。

cpp

#include <iostream>
using namespace std;// 第一个基类
class Printer {
public:void print() {cout << "Printing..." << endl;}
};// 第二个基类
class Scanner {
public:void scan() {cout << "Scanning..." << endl;}
};// 派生类
class MultiFunctionPrinter : public Printer, public Scanner {
public:void copy() {cout << "Copying..." << endl;}
};int main() {MultiFunctionPrinter mfp;mfp.print(); // 从Printer基类继承的方法mfp.scan();  // 从Scanner基类继承的方法mfp.copy();  // 自己定义的方法return 0;
}

4. 构造与析构

在多继承中,派生类的构造函数会依次调用每个基类的构造函数。析构函数的调用顺序是相反的,先调用派生类的析构函数,然后是基类的析构函数。

cpp

class Base1 {
public:Base1() { cout << "Base1 Constructor" << endl; }~Base1() { cout << "Base1 Destructor" << endl; }
};class Base2 {
public:Base2() { cout << "Base2 Constructor" << endl; }~Base2() { cout << "Base2 Destructor" << endl; }
};class Derived : public Base1, public Base2 {
public:Derived() { cout << "Derived Constructor" << endl; }~Derived() { cout << "Derived Destructor" << endl; }
};int main() {Derived d; // 构造顺序:Base1 -> Base2 -> Derivedreturn 0;  // 析构顺序:Derived -> Base2 -> Base1
}

5. 二义性问题

多继承的一个主要问题是二义性,即如果多个基类有同名成员,派生类将无法决定使用哪个基类的成员。这种情况需要通过作用域解析运算符来解决。

cpp

class A {
public:void show() {cout << "A::show()" << endl;}
};class B {
public:void show() {cout << "B::show()" << endl;}
};class C : public A, public B {
public:void display() {A::show(); // 使用A的showB::show(); // 使用B的show}
};int main() {C c;c.display();return 0;
}

6. 虚基类

为了解决多继承中的二义性问题,C++提供了虚基类的概念。虚基类确保在多重继承中只创建一个基类的实例,从而避免二义性。

cpp

class A {
public:void show() {cout << "A::show()" << endl;}
};class B : virtual public A {
public:void show() {cout << "B::show()" << endl;}
};class C : virtual public A {
public:void show() {cout << "C::show()" << endl;}
};class D : public B, public C {
public:void display() {A::show(); // 确保只调用一个A的实例}
};int main() {D d;d.display();return 0;
}

7. 优缺点

优点

  1. 灵活性:多继承允许组合多个类的功能,增加了类的灵活性和复用性。
  2. 功能扩展:可以通过继承多个基类来扩展功能,满足复杂的需求。
  3. 代码复用:通过多继承可以重用多个基类中的代码,减少代码重复。

缺点

  1. 复杂性:多继承可能导致类层次结构变得复杂,难以理解和维护。
  2. 二义性问题:当多个基类有同名成员时,会引起二义性,增加了使用难度。
  3. 内存开销:虚基类的使用可能导致内存开销增加。

8. 实际应用

多继承常用于需要组合多个功能的场景,例如:

  • 接口组合:在设计API时,可以通过多继承组合多个接口。
  • 设备驱动:在硬件驱动开发中,可能需要同时继承多个设备的功能。
  • 游戏开发:在游戏开发中,可以通过多继承创建具有多个特性的角色或对象。

9. 设计模式中的多继承

在一些设计模式中,多继承被广泛应用,例如:

  • 适配器模式:通过多继承实现不同接口的适配。
  • 桥接模式:将抽象部分与实现部分分离,可以通过多继承实现不同的实现。

注意事项

在使用多继承时,有几个注意事项:

  1. 清晰性:确保类的设计是清晰的,避免不必要的复杂性。
  2. 避免滥用:多继承虽然强大,但不应随意使用,应根据需要合理设计。
  3. 使用虚基类:在多重继承时,如果存在共同基类,考虑使用虚基类以避免二义性。

10. 总结

C++中的多继承是面向对象编程的重要特性,允许一个类同时继承多个基类的功能。尽管多继承提供了灵活性和代码复用,但也带来了复杂性和潜在的二义性问题。在设计类层次结构时,开发者应考虑是否真的需要多继承,权衡其优缺点,以实现更高效、更可维护的代码。

C++虚继承详解

1. 基础概念

虚继承是C++中处理多重继承的一种机制,旨在解决多继承时可能出现的二义性问题。二义性问题出现在两个或多个基类继承自同一个父类时,派生类会继承多个父类的实例,导致相同成员的冲突。虚继承确保在多重继承中只创建一个基类的实例,从而有效避免这种冲突。

2. 语法结构

在C++中,使用关键字 virtual 来声明虚基类。基本语法如下:

cpp

class Base {// 基类内容
};class Derived1 : virtual public Base {// 派生类1
};class Derived2 : virtual public Base {// 派生类2
};class FinalDerived : public Derived1, public Derived2 {// 最终派生类
};

3. 访问权限

与普通继承相同,虚继承也支持访问权限控制。可以为虚基类指定 publicprotectedprivate,以控制派生类对基类成员的访问权限。

示例代码

以下是一个简单的虚继承示例,展示了如何使用虚继承来解决二义性问题。

cpp

#include <iostream>
using namespace std;// 基类
class Base {
public:void show() {cout << "Base class show()" << endl;}
};// 第一个派生类
class Derived1 : virtual public Base {
public:void show() {cout << "Derived1 class show()" << endl;}
};// 第二个派生类
class Derived2 : virtual public Base {
public:void show() {cout << "Derived2 class show()" << endl;}
};// 最终派生类
class FinalDerived : public Derived1, public Derived2 {
public:void display() {// 显示调用的基类方法Base::show(); // 调用Base类的方法}
};int main() {FinalDerived fd;fd.display(); // 调用显示方法return 0;
}

5. 虚继承的工作机制

共享基类实例

虚继承的核心在于确保派生类只拥有一个基类的实例。对于上述示例中的 Base 类,Derived1Derived2 都虚继承自 Base,在 FinalDerived 中只会有一个 Base 的实例。这通过在内存中使用虚表(vtable)来实现。

构造顺序

在虚继承的情况下,构造函数的调用顺序如下:

  1. 先构造虚基类的实例(即 Base)。
  2. 然后构造派生类(如 Derived1Derived2)。
  3. 最后构造最终派生类(FinalDerived)。

6. 优缺点

优点

  1. 消除二义性:虚继承确保在多继承中只有一个基类实例,从而消除二义性。
  2. 代码复用:允许多个派生类共享父类的实现,减少代码重复。
  3. 清晰的层次结构:虚继承提供了更清晰的类层次结构,易于管理。

缺点

  1. 复杂性:引入虚继承可能使类的设计变得更加复杂,增加理解和维护的难度。
  2. 性能开销:使用虚继承会增加内存开销,因为需要维护虚表和指针。
  3. 构造与析构的复杂性:构造和析构顺序的特殊处理可能导致混淆。

7. 实际应用

虚继承多用于以下几种场景:

  1. 接口设计:在设计需要多个接口的类时,虚继承可以确保接口的唯一性。
  2. 多重继承:当多个类需要继承同一基类时,虚继承可以避免重复实例化,减少内存使用。
  3. 复杂系统:在复杂系统中,尤其是涉及多个模块和组件的系统,虚继承可以帮助管理类之间的关系。

注意事项

在使用虚继承时,需要注意以下几点:

  1. 合理使用:只有在确实需要解决二义性问题时,才应使用虚继承。
  2. 清晰设计:确保类的设计清晰,避免不必要的复杂性。
  3. 构造函数:理解虚基类的构造函数调用顺序,以避免潜在的错误。

示例扩展

以下是一个更复杂的示例,展示了虚继承的灵活性和强大功能。

cpp

#include <iostream>
using namespace std;// 基类
class Vehicle {
public:Vehicle() { cout << "Vehicle constructed." << endl; }void drive() { cout << "Driving a vehicle." << endl; }
};// 虚基类
class Car : virtual public Vehicle {
public:Car() { cout << "Car constructed." << endl; }void honk() { cout << "Car honking!" << endl; }
};// 虚基类
class Truck : virtual public Vehicle {
public:Truck() { cout << "Truck constructed." << endl; }void load() { cout << "Loading truck." << endl; }
};// 最终派生类
class Pickup : public Car, public Truck {
public:Pickup() { cout << "Pickup constructed." << endl; }
};int main() {Pickup p;p.drive(); // 调用Vehicle类的方法p.honk();  // 调用Car类的方法p.load();  // 调用Truck类的方法return 0;
}

8. 设计模式中的虚继承

在一些设计模式中,虚继承的应用也相当普遍。例如:

  • 适配器模式:通过虚继承组合不同类型的接口,使得对象可以互相适配。
  • 桥接模式:将抽象部分与实现部分分离,使用虚继承可以实现不同的实现。

9. 进阶概念

虚析构函数

为了安全地删除通过基类指针指向的派生类对象,基类应使用虚析构函数。这确保在删除时会调用派生类的析构函数。

cpp

class Base {
public:virtual ~Base() { cout << "Base Destructor" << endl; }
};class Derived : public Base {
public:~Derived() { cout << "Derived Destructor" << endl; }
};int main() {Base* b = new Derived();delete b; // 正确调用Derived和Base的析构函数return 0;
}

10. 总结

C++中的虚继承是解决多继承中二义性问题的重要机制。通过确保在多重继承中只创建一个基类的实例,虚继承提高了代码的可复用性和管理性。然而,虚继承引入的复杂性和性能开销也不容忽视。在设计类层次结构时,应合理考虑虚继承的使用,以实现更高效和可维护的代码结构。

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

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

相关文章

C语言 文件操作——按行读写文件

目录 按行写文件 按行读文件 按行读写文件 按行写文件 int puts ( const char *s ); 将字符串 s 写入标准输出流 stdout &#xff0c;并在其后添加一个换行符 按字符串&#xff08;行&#xff09; 写 文件 int fputs ( const char *s, FILE *fp); 将字符串 s 写入 fp 所…

轻松上手:使用 Vercel 部署 HTML 页面教程

&#x1f600; 在学习前端的过程中&#xff0c;部署项目往往是一个令人头疼的问题。然而&#xff0c;Vercel 为我们提供了一个便捷且免费的解决方案。 Vercel 是一个强大的云平台&#xff0c;专门用于前端项目的部署和托管。它不仅支持多种前端框架和静态网站生成器&#xff0…

【AI系列】Paddle Speech安装指南

文章目录 环境依赖1. 安装Python1.1 下载Python安装包1.2 安装gcc1.3 安装依赖库1.4 编译和安装Python1.5 配置环境变量 2. 安装PaddlePaddle3. 安装PaddleSpeech4. 运行PaddleSpeech5. 解决常见问题5.1 错误&#xff1a;libssl.so.1.1解决方法&#xff1a; 5.2 错误&#xff1…

2-6-1 关于“QNX Neutrino 编程入门”的前言

阅读前言 本文以QNX系统官方的文档英文原版资料“Getting Started with QNX Neutrino: A Guide for Realtime Programmers”为参考&#xff0c;翻译和逐句校对后&#xff0c;对在QNX操作系统下进行应用程序开发及进行资源管理器编写开发等方面&#xff0c;进行了深度整理&…

【中标麒麟服务器操作系统实例分享】java应用DNS解析异常分析及处理

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://documentkylinos.cn 情况描述 中标麒麟服务器操作系统V7运行在 ARM虚…

StarRocks 排查单副本表

文章目录 StarRocks 排查单副本表方式1 查询元数据&#xff0c;检查分区级的副本数方式2 SHOW PARTITIONS命令查看 ReplicationNum修改副本数命令 StarRocks 排查单副本表 方式1 查询元数据&#xff0c;检查分区级的副本数 # 方式一 查询元数据&#xff0c;检查分区级的副本数…

基于Transformer的自编码器模型在故障检测中的应用

在现代工业和制造领域&#xff0c;故障检测是保证设备和生产线安全、高效运行的关键。传统的故障检测方法往往依赖于人工经验或规则&#xff0c;然而&#xff0c;这些方法的准确性和泛化能力有限。随着深度学习技术的迅速发展&#xff0c;越来越多的智能故障检测方法应运而生&a…

《XML》教案 第2章 使第4章 呈现XML文档

《XML》教案 第2章 使第4章 呈现XML文档 主讲人&#xff1a; 回顾上一章: [10分钟] 2 课程知识点讲解&#xff1a; 2 通过级联样式表转换XML文档&#xff1a;[15分钟] 3 通过可扩展样式表语言转换XML文档 &#xff1a;[5分钟] 4 嵌套 for 循环 &#xff1a;[20分钟] 5 本章总结…

HBase、Hive、Redis 和 MongoDB的对比

1. 数据库管理 操作HBaseHiveRedisMongoDB创建数据库N/A (HBase 没有数据库概念)CREATE DATABASE db_name;N/A (Redis 没有数据库命名功能)use db_name; (自动创建)查看数据库N/ASHOW DATABASES;INFO 查看全局信息show dbs;删除数据库N/ADROP DATABASE db_name CASCADE;N/Adb.…

Sigrity System Explorer Snip Via Pattern From Layout模式从其它设计中截取过孔模型和仿真分析操作指导

Sigrity System Explorer Snip Via Pattern From Layout模式从其它设计中截取过孔模型和仿真分析操作指导 Sigrity System Explorer Snip Via Pattern From Layout模式支持从其它设计中截取过孔模型用于仿真分析,同样以差分模板为例 具体操作如下 双击打开System Explorer软件…

数据结构_平衡二叉树

结点类 构造函数分为有参和无参&#xff0c;相同点都是初始化树高为1 class Node { public:int data; // 用于输出int val; // 数据域&#xff0c;用于排序int height; // 树高Node* left;Node* right;Node();Node(int v, int d);static int max(int a, int b); };Node::N…

2024年度个人总结

一转眼已经2024年度最后一个月了&#xff0c;今年基本没有在CSDN发布内容&#xff0c;包括其他平台&#xff08;B站&#xff09;&#xff0c;倒是在其他地方&#xff08;我的个人网站和V2EX&#xff09;发布一些零碎的东西&#xff0c;主要是因为今年换了工作后太累了&#xff…

汽车IVI中控开发入门及进阶(42):OpenVG

概览: OpenVG是一个无版权、跨平台的API,它为高级用户界面和矢量图形库(如SVG)提供了一个低级硬件加速接口。OpenVG主要针对需要便携式加速高质量矢量图形以获得引人注目的用户界面和文本的消费电子产品、手持设备、可穿戴设备和汽车设备,同时使硬件加速能够在非常低的功…

基于微信小程序的消防隐患在线举报系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了多年的设计程序开发&#xff0c;开发过上千套设计程序&#xff0c;没有什么华丽的语言&#xff0c;只有实…

如何重新设置VSCode的密钥环密码?

故障现象&#xff1a; 忘记了Vscode的这个密码&#xff1a; Enter password to unlock An application wants access to the keyring “Default ke... Password: The unlock password was incorrect Cancel Unlock 解决办法&#xff1a; 1.任意terminal下&#xff0c;输入如下…

springcloud-gateway获取应用响应信息乱码

客户端通过springcloud gateway跳转访问tongweb上的应用&#xff0c;接口响应信息乱码。使用postman直接访问tongweb上的应用&#xff0c;响应信息显示正常。 用户gateway中自定义了实现GlobalFilter的Filter类&#xff0c;在该类中获取了上游应用接口的响应信息&#xff0c;直…

JS萤石云录像回放拖动进度条无法正常播放

问题描述&#xff1a; 本项目版本&#xff1a;vue2.6.12&#xff0c;webpack3.6.0&#xff0c;ezuikit-js0.7.2 在使用萤石云的JavaScript SDK做监控的直播、录像回放时&#xff0c;遇到部分设备的录像回放&#xff0c;无法根据控制面板的拖动进度条查看某时间段的录像。 官方…

2024.12.21辩论赛感受

背景 今天辩论赛的双方论点是&#xff1a; 正方&#xff1a;寒假留在研发中心的收获大 反方&#xff1a;寒假去做其他事情的收获 辩论赛&#xff0c;为了锻炼自己&#xff0c;选择了不想选择以及相对不好辩论的反方。出现的状况有一下几点&#xff1a; 1.发现自己脑子完全跟不…

混元大模型简介及个人运行方案

一、混元大模型简介 混元大模型&#xff08;HunyuanVideo&#xff09;是一个开源的视频生成基础模型&#xff0c;专为高质量的视频生成任务设计。它融合了多项先进技术和创新架构&#xff0c;在视觉质量、多样性、文本与视频的对齐度&#xff0c;以及生成的稳定性方面表现出色…

图漾相机-ROS1_SDK_ubuntu版本编译(新版本)

文章目录 官网编译文档链接官网SDK下载链接1、下载 Camport ROS1 SDK1.下载git2、下载链接 2、准备编译工作1、安装 catkin2、配置环境变量3. 将Camport3中的linux库文件拷贝到 user/lib目录下4、修改lunch文件制定相机&#xff08;可以放在最后可以参考在线文档&#xff09;**…