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

【C++进阶】深入了解继承

文章目录

  • 1.继承与友元
  • 2. 继承与静态成员
  • 3. 多继承及其菱形继承问题
    • 3.1 继承模型
    • 3.2 虚继承
    • 3.3 多继承中指针偏移问题
    • 3.4 IO库中的菱形虚拟继承
  • 4. 继承和组合
    • 4.1 继承和组合

点击跳转继承前置知识

1.继承与友元

友元关系不能继承,也就是说基类友元不能访问派生类私有和保护成员。

//编译器从前往后找,所以加上前置声明classStudent;classPerson{public:friendvoidDisplay(constPerson&p,constStudent&s);protected:string _name;// 姓名};classStudent:publicPerson{protected:int_stuNum;// 学号};voidDisplay(constPerson&p,constStudent&s){cout<<p._name<<endl;cout<<s._stuNum<<endl;}intmain(){Person p;Student s;// 编译报错:error C2248: “Student::_stuNum”: 无法访问 protected 成员// 解决方案:Display也变成Student的友元即可Display(p,s);return0;}

2. 继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个派生类,都只有一个static成员实例。

classPerson{public:string _name;staticint_count;};intPerson::_count=0;classStudent:publicPerson{protected:int_stuNum;};intmain(){Person p;Student s;// 这里的运行结果可以看到非静态成员_name的地址是不一样的// 说明派生类继承下来了,父派生类对象各有一份cout<<&p._name<<endl;cout<<&s._name<<endl;// 这里的运行结果可以看到静态成员_count的地址是一样的// 说明派生类和基类共用同一份静态成员cout<<&p._count<<endl;cout<<&s._count<<endl;// 公有的情况下,父派生类指定类域都可以访问静态成员cout<<Person::_count<<endl;cout<<Student::_count<<endl;return0;}

3. 多继承及其菱形继承问题

3.1 继承模型

单继承:一个派生类只有一个直接基类时称这个继承关系为单继承

多继承:一个派生类有两个或以上直接基类时称这个继承关系为多继承,多继承对象在内存中的模型是,先继承的基类在前面,后面继承的基类在后面,派生类成员在放到最后面。

菱形继承:菱形继承是多继承的一种特殊情况。菱形继承的问题,从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题,在Assistant的对象中Person成员会有两份。支持多继承就一定会有菱形继承,像Java就直接不支持多继承,规避掉了这里的问题,所以实践中我们也是不建议设计出菱形继承这样的模型的。

classPerson{public:string _name;// 姓名};classStudent:publicPerson{protected:int_num;//学号};classTeacher:publicPerson{protected:int_id;// 职工编号};classAssistant:publicStudent,publicTeacher{protected:string _majorCourse;// 主修课程};intmain(){// 编译报错:error C2385: 对“_name”的访问不明确Assistant a;a._name="peter";// 需要显示指定访问哪个基类的成员可以解决二义性问题,但是数据冗余问题无法解决a.Student::_name="xxx";a.Teacher::_name="yyy";return0;}

3.2 虚继承

很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂,性能也会有一些损失,所以最好不要设计出菱形继承

classPerson{public:string _name;// 姓名/*int _tel; int _age; string _gender; string _address;*/// ...};// 使用虚继承Person类classStudent:virtualpublicPerson{protected:int_num;//学号};// 使用虚继承Person类classTeacher:virtualpublicPerson{protected:int_id;// 职工编号};// 教授助理classAssistant:publicStudent,publicTeacher{protected:string _majorCourse;// 主修课程};intmain(){// 使用虚继承,可以解决数据冗余和二义性Assistant a;a._name="peter";return0;}

解决办法:


下面这幅图也是菱形继承

classPerson{public:Person(constchar*name):_name(name){}string _name;// 姓名};classStudent:virtualpublicPerson{public:Student(constchar*name,intnum):Person(name),_num(num){}protected:int_num;//学号};classTeacher:virtualpublicPerson{public:Teacher(constchar*name,intid):Person(name),_id(id){}protected:int_id;// 职⼯编号};// 不要去玩菱形继承classAssistant:publicStudent,publicTeacher{public:Assistant(constchar*name1,constchar*name2,constchar*name3):Person(name3),Student(name1,1),Teacher(name2,2){}protected:string _majorCourse;// 主修课程};intmain(){// 思考⼀下这⾥a对象中_name是"张三", "李四", "王五"中的哪⼀个?Assistanta("张三","李四","王五");return0;}

实际上,结果输出王五,因为会按照初始化列表顺序调用,先调用Person类,调用Student和Teacher类的构造函数时,不会再次调用Person类的构造函数,所以结果是王五

3.3 多继承中指针偏移问题

下面说法正确的是( )
A:p1 == p2 == p3 B:p1 < p2 < p3 C:p1 == p3 != p2 D:p1 != p2 != p3

classBase1{public:int_b1;};classBase2{public:int_b2;};classDerive:publicBase1,publicBase2{public:int_d;};intmain(){Derive d;Base1*p1=&d;Base2*p2=&d;Derive*p3=&d;return0;}

选C

3.4 IO库中的菱形虚拟继承

template<classCharT,classTraits=std::char_traits<CharT>>classbasic_ostream:virtualpublicstd::basic_ios<CharT,Traits>{};template<classCharT,classTraits=std::char_traits<CharT>>classbasic_istream:virtualpublicstd::basic_ios<CharT,Traits>{};

4. 继承和组合

4.1 继承和组合

public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。

组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。

继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对派生类可见。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。

对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。

优先使用组合,而不是继承。实际尽量多去用组合,组合的耦合度低,代码维护性好。不过也不太那么绝对,类之间的关系就适合继承(is-a)那就用继承,另外要实现多态,也必须要继承。类之间的关系既适合用继承(is-a)也适合组合(has-a),就用组合。

// Tire(轮胎)和Car(车)更符合has-a的关系classTire{protected:string _brand="Michelin";// 品牌size_t _size=17;// 尺寸};classCar{protected:string _colour="白色";// 颜色string _num="陕ABIT00";// 车牌号Tire _t1;// 轮胎Tire _t2;// 轮胎Tire _t3;// 轮胎Tire _t4;// 轮胎};classBMW:publicCar{public:voidDrive(){cout<<"好开-操控"<<endl;}};// Car和BMW/Benz更符合is-a的关系classBenz:publicCar{public:voidDrive(){cout<<"好坐-舒适"<<endl;}};template<classT>classvector{};// stack和vector的关系,既符合is-a,也符合has-atemplate<classT>classstack:publicvector<T>{};template<classT>classstack{public:vector<T>_v;};intmain(){return0;}

http://www.jsqmd.com/news/861388/

相关文章:

  • IDEA 索引构建卡死在 99% 进度不动了如何强制重置?
  • 语音搜索 GEO 优化,口语化英文短句布局玩法
  • 本地能跑线上崩?MonkeyCode统一云端环境解决团队开发噩梦
  • 【深耕GEO服务,赋能数字升级——西安群蜂云计算,优质GEO服务领航者】
  • 深度解析DDoS攻击:运作机制与防御体系构建
  • 卖 MATLAB 工具箱,你的代码可能正被免费白嫖——聊聊商业化前的代码保护
  • 大模型实战:AgentScope ReActAgent 多智能体框架实战指南,小白程序员必备收藏!
  • Spark 从入门到部署:核心模块解析与 Yarn 模式实战指南
  • BOSCH SM50/100-T伺服驱动器
  • 《数据主权时代,企业协作需切换“私有模式”》
  • 【优化 v 2.7.5 版本】PC 端 Open Claw 一键部署详细教学
  • 家居建材营销新趋势:数字化、体验式与可持续方向-佛山鼎策创局破局增长咨询有限公司
  • ⾯向对象和集合编程题
  • 在鸿蒙上跑一个端侧大模型——不用连云端数据全在本地
  • Java Comparator深度解析:从底层原理到实战应用
  • 内存管理与垃圾回收原理及机器学习实验研究
  • 一篇文章讲清楚—Windows 电脑中 CMD 和 PowerShell 有啥区别
  • 从CRUD到AI大神:小白程序员5个月逆袭之路(收藏版)
  • WorkBuddy:一个面向内容创作的桌面自动化助手实践
  • 1794-ACN15适配器模块
  • ComfyUI v0.22.0 更新:工作流模板升级、音频与多模态增强、OpenAPI 文档完善、节点能力大幅扩展
  • 2026年4月制冷厂推荐:制冷机组、制冷设备品牌、南宁制冷、反应釜制冷设备、商丘制冷、太原制冷、安徽冷水机、安徽制冷选择指南 - 优质品牌商家
  • Agent 认知破局:从具象表象到交互本质
  • EPRO MMS6120振动检测模块
  • 2026丛林穿越厂家怎么选:户外丛林穿越厂家、无动力乐园规划设计、无动力游乐设备非标定制、游乐场无动力游乐设备选择指南 - 优质品牌商家
  • 抖音获客失效?拆解本地商家流量困局的底层逻辑与破局路径
  • Linux 硬盘分区管理
  • 高性价比塑料链板输送机厂家排行适配指南
  • c语言中语句分类
  • Chiplet经济学:成本如何影响芯片产业发展?