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

C++:组合和继承的区别

组合介绍以及与继承对比

什么是组合
(1)composition,组合,就是在一个class内使用其他多个class的对象作为成员
(2)用class tree做案例讲解
(3)组合也是一种代码复用方法,本质也是结构体包含

#include <iostream>
#include <vector>
#include <string>// Leaf 类
class Leaf {
public:Leaf(const std::string& color) : color_(color) {std::cout << "Leaf constructor called: " << color_ << std::endl;}~Leaf() {std::cout << "Leaf destructor called: " << color_ << std::endl;}void display() const {std::cout << "Leaf color: " << color_ << std::endl;}private:std::string color_;
};// Branch 类
class Branch {
public:Branch(int length) : length_(length) {std::cout << "Branch constructor called: " << length_ << " cm" << std::endl;}~Branch() {std::cout << "Branch destructor called: " << length_ << " cm" << std::endl;}void display() const {std::cout << "Branch length: " << length_ << " cm" << std::endl;}private:int length_;
};// Tree 类,包含 Leaf 和 Branch 对象
class Tree {
public:Tree(const std::string& leafColor, int branchLength) : leaf_(leafColor), branch_(branchLength) {std::cout << "Tree constructor called" << std::endl;}~Tree() {std::cout << "Tree destructor called" << std::endl;}void display() const {leaf_.display();branch_.display();}private:Leaf leaf_;Branch branch_;
};int main() {Tree tree("Green", 150);tree.display();return 0;
}

在这里插入图片描述

继承与组合的特点对比
(1)继承是a kind of(is a)关系,具有传递性,不具有对称性。
(2)组合是a part of(has a)的关系,
(3)继承是白盒复用。因为类继承允许我们根据自己的实现来覆盖重写父类的实现细节,父类的实现对于子类是可见的。
(4)继承的白盒复用特点,一定程度上破坏了类的封装特性,因为这会将父类的实现细节暴露给子类
(5)组合属于黑盒复用。被包含对象的内部细节对外是不可见的,所以它的封装性相对较好,实现上相互依赖比较小
(6)组合中被包含类会随着包含类创建而创建,消亡而消亡。组合属于黑盒复用,并且可以通过获取其它具有相同类型的对象引用或指针,在运行期间动态的定义组合。而缺点就是致使系统中的对象过多。
(7)OO设计原则是优先组合,而后继承

多继承及其二义性问题

多继承
(1)多继承就是一个子类有多个父类
(2)多继承演示
(3)多继承和单继承的原理,效果并无明显区别
(4)多继承会导致二义性问题
多继承的二义性问题1
(1)场景:C多继承自A和B,则C中调用A和B的同名成员时会有二义性
(2)原因:C从A和B各自继承了一个同名(不同namespace域)成员,所以用C的对象来调用时编译器无法确定我们想调用的是哪一个
(3)解决办法1:避免出现,让A和B的public成员命名不要重复冲突。但这个有时不可控。
(4)解决办法2:编码时明确指定要调用哪一个,用c.A::func()明确指定调用的是class A的func而不是class B的
(5)解决办法3:在C中重定义func,则调用时会调用C中的func,A和B中的都被隐藏了
(6)总结:能解决,但是都没有很好的解决。

多继承的二义性问题2
(1)场景:菱形继承问题。即A为祖类,B1:A, B2:A, C:B1,B2,此时用C的对象调用A中的某个方法时会有二义性
(2)分析:c.func()有二义性,c.A::func()也有二义性,但是c.B1::func()和c.B2::func()却没有二义性
(3)解决办法:和问题1中的一样,但是问题2更隐蔽,也更难以避免

在这里插入图片描述

// 祖类
class Aa {public:void show() { std::cout << "A's method" << std::endl; }
};// 派生类 B1 和 B2,从 A 继承
class B1 : public Aa {public:void show() { std::cout << "B1's show" << std::endl; }void showB1() { std::cout << "B1's method" << std::endl; }
};class B2 : public Aa {public:void show() { std::cout << "B2's show" << std::endl; }void showB2() { std::cout << "B2's method" << std::endl; }
};// 派生类 C,从 B1 和 B2 继承
class C : public B1, public B2 {public:void showC() { std::cout << "C's method" << std::endl; }
};int test070103() {C c;// c.Aa::show();    // 调用 A 的方法 不可以c.Aa::B1::show();  // 调用 A 的方法 不可以c.B2::Aa::show();  // 调用 A 的方法 不可以c.B1::show();      // 调用 B1 的方法c.B2::show();      // 调用 B2 的方法c.showB1();        // 调用 B1 的方法c.showB2();        // 调用 B2 的方法c.showC();         // 调用 C 的方法return 0;
}

虚继承解决菱形继承的二义性问题

虚继承怎么用
(1)场景:菱形继承导致二义性问题,本质上是在孙子类C中有B1和B2中包含的2份A对象,所以有了二义性。
(2)虚继承解决方案:让B1和B2虚继承A,C再正常多继承B1和B2即可
(3)虚继承就这么简单,就是为了解决菱形继承的二义性问题而生,和虚函数(为了实现多态特性)并没有直接关系

虚继承的实现原理
(1)虚继承的原理是:虚基类表指针vbptr和虚基类表virtual table
(2)参考:https://blog.csdn.net/xiejingfa/article/details/48028491

在这里插入图片描述

总结

理解什么是组合,一个class类里面有很多其他class类类型的成员变量
理解组合和继承的区别
二义性:执行了一个函数,确不一定是自己想指定的那个
解决方案:用c.A::func()明确的指定调用的是class
虚继承有点像条件编译,引入一次就好,不用重复引入,也能达到引入效果

学习记录,侵权联系删除。
来源:朱老师物联网大课堂

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • LeetCode HOT100(三)滑动窗口
  • 【Spring】springSecurity中WebSecurityConfigurerAdapter类中configure方法(5版本以下)
  • 2022 RoboCom省赛题目解析
  • Git: fatal: cannot lock ref‘HEAD‘: Unable to create
  • SQL 存储过程
  • 短视频矩阵:批量发布的秘密揭秘
  • SpringBoot常见注解
  • 数列分块<2>
  • int类型变量表示范围的计算原理
  • RISC-V指令集架构详细组成
  • ASP.NET Core 使用Log4net
  • elasticSearch快速了解
  • shark云原生-日志体系-ECK
  • 基于前馈神经网络 FNN 实现股票单变量时间序列预测(PyTorch版)
  • 打卡第9天-----字符串
  • 【划重点】MySQL技术内幕:InnoDB存储引擎
  • 78. Subsets
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • create-react-app做的留言板
  • ECMAScript入门(七)--Module语法
  • git 常用命令
  • iOS 系统授权开发
  • Java IO学习笔记一
  • java2019面试题北京
  • java8-模拟hadoop
  • Java应用性能调优
  • laravel5.5 视图共享数据
  • MySQL QA
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • Redis 懒删除(lazy free)简史
  • Redis学习笔记 - pipline(流水线、管道)
  • Vue2.x学习三:事件处理生命周期钩子
  • 不上全站https的网站你们就等着被恶心死吧
  • 第十八天-企业应用架构模式-基本模式
  • 马上搞懂 GeoJSON
  • 前端之React实战:创建跨平台的项目架构
  • 使用 Docker 部署 Spring Boot项目
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 通信类
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • # Kafka_深入探秘者(2):kafka 生产者
  • # Redis 入门到精通(一)数据类型(4)
  • # 数论-逆元
  • $NOIp2018$劝退记
  • (1/2)敏捷实践指南 Agile Practice Guide ([美] Project Management institute 著)
  • (2024,Flag-DiT,文本引导的多模态生成,SR,统一的标记化,RoPE、RMSNorm 和流匹配)Lumina-T2X
  • (安卓)跳转应用市场APP详情页的方式
  • (笔记)M1使用hombrew安装qemu
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (论文阅读22/100)Learning a Deep Compact Image Representation for Visual Tracking
  • (每日一问)设计模式:设计模式的原则与分类——如何提升代码质量?
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (十)Flink Table API 和 SQL 基本概念
  • (十二)Flink Table API