尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

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

【C/C++】多继承以及继承过程的注意事项
📅 发布时间:2026/6/20 12:07:35

是的,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),在运行时更灵活

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

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

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

相关新闻

  • 【MCP SC-400安全加固黄金法则】:3年实战经验浓缩的7项配置规范
  • xshell的一个会话的连接的ip地址在哪里修改?
  • Day9 >> 151、反转字符串中的单词 +

最新新闻

  • 2026淮南中考100-200分,不用辍学,本地公办学校正规招录 - 我叫小周
  • XXMI启动器:一站式游戏模组管理终极指南,告别繁琐配置的完整解决方案
  • 如何制作微信图文投票活动?简易图文教程2026最新版(免费防刷+批量导入) - 微信投票小程序
  • Kali Linux安装配置Burp Suite专业版完整指南
  • 2026年6月最新江诗丹顿中国官方售后服务电话地址热线及客服网点 - 江诗丹顿服务中心
  • 2026 年 6 月最新腕表干货!万国全大陆官方正规维修门店地址完整公示,全国统一售后热线同步全新上线 - 万国中国服务中心

日新闻

  • 信任的进化:技术实现详解——如何用JavaScript构建博弈论模拟器
  • Terrakube自定义工作流:如何集成OPA、Infracost等工具扩展IaC能力
  • grunt-concurrent快速入门:5分钟学会并行运行Grunt任务

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号