当前位置: 首页 > news >正文

【C/C++】多继承以及继承过程的注意事项

是的,C++ 支持多继承(Multiple Inheritance),包括从多个虚拟类(抽象类)继承。

1. 基本的多继承语法

cpp
class Base1 {
public:virtual void func1() = 0;  // 纯虚函数
};class Base2 {
public:virtual void func2() = 0;  // 纯虚函数
};class Derived : public Base1, public Base2 {
public:void func1() override { /* 实现 */ }void func2() override { /* 实现 */ }
};

2. 虚拟继承(解决菱形继承问题)

当出现菱形继承时,需要使用虚拟继承来避免数据重复:

cpp
class Base {
public:int value;
};class Intermediate1 : virtual public Base {// 虚拟继承
};class Intermediate2 : virtual public Base {// 虚拟继承
};class Final : public Intermediate1, public Intermediate2 {// 只有一个 Base 实例
};int main() {Final obj;obj.value = 10;  // 没有歧义,只有一个 Base::value
}

3. 多继承的注意事项

a) 函数名冲突

cpp
class A {
public:void foo() { }
};class B {
public:void foo() { }  // 同名函数
};class C : public A, public B {
public:// 需要明确指定使用哪个父类的 foovoid callFoo() {A::foo();  // 明确调用 A 的 fooB::foo();  // 明确调用 B 的 foo}
};

b) 构造函数调用顺序

cpp
class Base1 {
public:Base1() { cout << "Base1\n"; }
};class Base2 {
public:Base2() { cout << "Base2\n"; }
};class Derived : public Base1, public Base2 {
public:Derived() : Base2(), Base1() {  // 初始化列表顺序不影响实际构造顺序cout << "Derived\n";}// 实际构造顺序:Base1 → Base2 → Derived
};

4. 接口类(推荐用法)

在实际开发中,通常建议使用纯抽象类作为接口进行多继承:

cpp
// 接口类(只有纯虚函数)
class IReader {
public:virtual void read() = 0;virtual ~IReader() = default;
};class IWriter {
public:virtual void write() = 0;virtual ~IWriter() = default;
};// 实现类
class FileIO : public IReader, public IWriter {
public:void read() override { /* 实现 */ }void write() override { /* 实现 */ }
};

5. 多继承的优缺点

优点:

  • 提高代码复用性

  • 更灵活地组合功能

  • 支持接口分离

缺点:

  • 增加复杂性

  • 可能导致菱形继承问题

  • 虚函数表更复杂

建议

  1. 优先使用组合而非继承

  2. 如果需要多继承,优先继承接口(纯抽象类)

  3. 使用虚拟继承解决菱形继承问题

  4. 明确解决函数名冲突

C++ 标准库中的 iostream 就是多继承的例子:

cpp
class iostream : public istream, public ostream {// 同时继承输入流和输出流
};

总之,C++ 完全支持从多个虚拟类(抽象类)继承,但需要谨慎使用,以避免设计上的复杂性。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
优先使用组合而非继承是什么意思?
 
 
 
 
 
 

“优先使用组合而非继承” 是面向对象设计的一个重要原则,意思是在设计类之间的关系时,应该优先考虑使用组合(composition),而不是继承(inheritance)

1. 什么是组合?什么是继承?

继承(is-a 关系)

cpp
// "Dog 是一个 Animal"
class Animal {
public:virtual void makeSound() = 0;
};class Dog : public Animal {  // 继承
public:void makeSound() override {cout << "Woof!" << endl;}
};

组合(has-a 关系)

cpp
// "Car 有一个 Engine"
class Engine {
public:void start() {cout << "Engine started" << endl;}
};class Car {
private:Engine engine;  // 组合:Car 包含 Engine 对象
public:void startCar() {engine.start();  // 委托给 Engine}
};

2. 为什么优先使用组合?

示例对比:继承的问题

继承方式(不灵活):

cpp
class Bird {
public:virtual void fly() {cout << "Flying" << endl;}virtual void eat() {cout << "Eating" << endl;}
};class Penguin : public Bird {
public:void fly() override {throw runtime_error("Penguins can't fly!");  // 问题:企鹅不会飞!}
};

组合方式(更灵活):

cpp
// 将功能分解为独立的类
class FlyBehavior {
public:virtual void fly() = 0;
};class EatBehavior {
public:virtual void eat() = 0;
};// 具体行为实现
class CanFly : public FlyBehavior {
public:void fly() override {cout << "Flying" << endl;}
};class CannotFly : public FlyBehavior {
public:void fly() override {cout << "Cannot fly" << endl;}
};// 鸟类使用组合
class Bird {
private:unique_ptr<FlyBehavior> flyBehavior;unique_ptr<EatBehavior> eatBehavior;public:Bird(unique_ptr<FlyBehavior> fb, unique_ptr<EatBehavior> eb): flyBehavior(move(fb)), eatBehavior(move(eb)) {}void performFly() {flyBehavior->fly();}void performEat() {eatBehavior->eat();}// 可以动态改变行为void setFlyBehavior(unique_ptr<FlyBehavior> fb) {flyBehavior = move(fb);}
};// 使用
int main() {// 燕子:会飞Bird swallow(make_unique<CanFly>(), make_unique<NormalEat>());// 企鹅:不会飞Bird penguin(make_unique<CannotFly>(), make_unique<NormalEat>());// 动态改变行为(继承做不到这点)penguin.setFlyBehavior(make_unique<CanFly>());  // 现在企鹅会飞了!
}

3. 组合的优势

a) 灵活性更高

cpp
// 继承:编译时绑定
class BasicReport : public Report { /* 固定的功能 */ }// 组合:运行时可以改变
class Report {
private:unique_ptr<Formatter> formatter;unique_ptr<Exporter> exporter;public:void setFormatter(unique_ptr<Formatter> fmt) {formatter = move(fmt);}// 可以随时切换为 JSONFormatter、XMLFormatter 等
};

b) 避免脆弱的基类问题

cpp
// 基类修改可能破坏所有子类
class Base {
public:void foo() { /* ... */ }
};// 如果 Base::foo() 实现改变,可能影响 Derived
class Derived : public Base {// 依赖 Base 的实现细节
};

c) 更好的封装性

cpp
class Car {
private:Engine engine;      // 实现细节被隐藏Transmission trans;BrakeSystem brakes;public:void drive() {engine.start();trans.shift();// 用户不需要知道内部细节}
};

4. 何时使用继承?

继承仍有其适用场景:

cpp
// 适用场景1:真正的 is-a 关系,且不会改变
class Shape {  // 抽象基类
public:virtual double area() const = 0;virtual ~Shape() = default;
};class Circle : public Shape {  // Circle 就是一个 Shape
public:double area() const override {return 3.14 * radius * radius;}
};// 适用场景2:需要多态
vector<unique_ptr<Shape>> shapes;
shapes.push_back(make_unique<Circle>());
shapes.push_back(make_unique<Rectangle>());for (auto& shape : shapes) {cout << shape->area() << endl;  // 多态调用
}

5. 设计原则总结

继承使用场景:

  1. 明确的 is-a 关系(企鹅是鸟 ×,正方形是形状 ✓)

  2. 需要多态行为

  3. 基类是稳定的抽象(不会频繁修改)

组合使用场景:

  1. has-a 或 uses-a 关系(汽车有引擎)

  2. 需要运行时灵活性

  3. 需要复用多个类的功能

  4. 避免类层次过深

实际建议:

cpp
// 不好的设计:过深的继承层次
class Vehicle {};
class LandVehicle : public Vehicle {};
class Car : public LandVehicle {};
class Sedan : public Car {};
class SportsSedan : public Sedan {};// 更好的设计:组合 + 浅继承
class Vehicle {
private:unique_ptr<Engine> engine;unique_ptr<Transmission> transmission;vector<unique_ptr<Feature>> features;  // 功能特性
};// 通过添加不同组件来创建不同车型
class CarFactory {
public:static Vehicle createSportsCar() {Vehicle v;v.setEngine(make_unique<V8Engine>());v.addFeature(make_unique<SportsSuspension>());return v;}
};

6. 实际应用:策略模式

cpp
// 使用组合实现策略模式
class PaymentStrategy {
public:virtual void pay(double amount) = 0;virtual ~PaymentStrategy() = default;
};class CreditCardPayment : public PaymentStrategy {
public:void pay(double amount) override { /* 信用卡支付逻辑 */ }
};class PayPalPayment : public PaymentStrategy {
public:void pay(double amount) override { /* PayPal 支付逻辑 */ }
};class ShoppingCart {
private:vector<Item> items;unique_ptr<PaymentStrategy> paymentStrategy;public:void setPaymentStrategy(unique_ptr<PaymentStrategy> strategy) {paymentStrategy = move(strategy);}void checkout() {double total = calculateTotal();paymentStrategy->pay(total);  // 使用组合的策略}
};

总结

"优先使用组合而非继承" 的核心思想是:

  • 继承表示"是什么"(is-a),在编译时确定关系

  • 组合表示"有什么"(has-a),在运行时更灵活

  • 组合降低耦合度,提高代码复用性和可维护性

  • 只有在真正需要多态代码共享,且关系稳定时,才使用继承

这个原则帮助创建更灵活、更易维护的系统设计。

http://www.rkmt.cn/news/83942.html

相关文章:

  • 【MCP SC-400安全加固黄金法则】:3年实战经验浓缩的7项配置规范
  • xshell的一个会话的连接的ip地址在哪里修改?
  • Day9 >> 151、反转字符串中的单词 +
  • 三星三折叠价格和功能揭秘:19999元起,三折叠旗舰藏多少惊喜?
  • 如何用免费工具3分钟终极优化Windows右键菜单:告别杂乱,提升300%操作效率
  • Wan2.2-T2V-A14B在地震波传播模拟教学中的科学准确性
  • Wan2.2-T2V-A14B能否生成符合人类视觉习惯的景深效果
  • Wan2.2-T2V-A14B在环保公益广告创作中的社会责任体现
  • 终极指南:5分钟掌握XUnity.AutoTranslator让外文游戏变中文
  • 2025 最新跨境物流服务商 / 公司 TOP5 评测!深度覆盖欧美加专线,全链路方案 + 时效保障权威榜单发布,赋能跨境电商高效发展 - 全局中转站
  • 2025 最新空运整柜拼箱/跨境物流/货代/外贸出口/货代方案服务商 TOP5 评测!深度覆盖欧美加专线,全链路赋能 + 实力实证权威榜单发布,专业服务重构跨境贸易生态 - 全局中转站
  • AXI-A7.4.1 AtomicCompare
  • C++的第十四天笔记
  • java学习——枚举类
  • 2025必藏:中低压+低压+中压电缆厂家榜单,实力品牌全在这 - 品牌2026
  • 康迪科技收购美国高端电动越野摩托车品牌,加速多品牌战略布局
  • Comsol 光子晶体仿真:拓扑荷、调控merging BIC与相关计算
  • 无监督学习的现代应用:聚类与异常检测在真实业务场景中的落地
  • 12.11 - 最长回文子串 main函数是如何开始的
  • 基础数据结构:栈、队列、链表
  • Docker + 多模态Agent = 王炸组合?5个真实生产环境编排案例深度剖析
  • 如何为你的Python项目构建pyproject.toml文件
  • 基于SpringBoot的学生学习成果展示平台的实现-计算机毕业设计源码+LW文档分享
  • 护网行动关键方向深度分享:应急响应 / 云原生防护等五大领域文章 + 实战技巧,附案例拆解!
  • 你用过哪些国产实时数据库?
  • ISO20000新版标准深度解析:5大关键变化如何重塑IT服务管理
  • 离网下基于下垂控制的 T 型三电平逆变器研究
  • 运维系列Windows系列【仅供参考】:Win11暂停更新点不了怎么办?Win11暂停更新是灰色的如何解决?
  • HCNP学习第五天打卡
  • 12/10到12/11的做题总结