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

【C++】继承(2):继承与友元,静态成员,多继承黑/白盒复用

目录

一继承与友元

二 继承与静态成员

三 多继承及其菱形继承问题

1 继承模型

2 菱形继承

3 虚继承

4 io库中的菱形继承

四 继承与组合

1 继承和组合

2 示例

3 白盒复用&&黑盒复用

白盒复用(White-box Reuse)

黑盒复用(Black-box Reuse)

五 实现一个不能被继承的类

1 方法

2 例题


前文回顾:【C++】继承(1):深入理解和使用

一继承与友元

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

class Student;
class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名 
};class Student : public Person
{
protected:int _stuNum; // 学号 
};
void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl;
}
int main()
{Person p;Student s;// 编译报错:error C2248: “Student::_stuNum”: ⽆法访问 protected 成员 // 解决⽅案:Display也变成Student 的友元即可 Display(p, s);return 0;
}

注意:这里class student 的前置声明不能省略,不然会报错

解决方案:在class student 中也加入友元函数display


二 继承与静态成员

基类定义了static静态成员,则整个继承体系⾥⾯只有⼀个这样的成员。⽆论派⽣出多少个派⽣类,都 只有⼀个static成员实例。

class Person
{
public:string _name;static int _count;
};int Person::_count = 0;class Student : public Person
{
protected:int _stuNum;
};int main()
{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;return 0;
}

  非静态成员:非静态成员的继承是父类和子类各一份,地址不一样。

  静态成员:静态成员的继承是父类和子类共用同一份,地址也一样。


三 多继承及其菱形继承问题

1 继承模型

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

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

示例:

但其实多继承是有问题的,是一个大坑!!!

2 菱形继承

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

多继承不是问题,多继承实现的菱形继承才是问题

class Person
{
public:
string _name; // 姓名 
};class Student : public Person
{
protected:int _num; //学号 
};class Teacher : public Person
{
protected:int _id; // 职⼯编号 
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程 
};int main()
{// 编译报错:error C2385: 对“_name”的访问不明确 Assistant a;a._name = "peter";// 需要显⽰指定访问哪个基类的成员可以解决⼆义性问题,但是数据冗余问题⽆法解决 a.Student::_name = "xxx";a.Teacher::_name = "yyy";return 0;
}

我们可以设计出多继承,但是不建议设计出菱形继承,因为菱形虚拟继承以后,⽆论是使⽤还是底层 都会复杂很多。当然有多继承语法⽀持,就⼀定存在会设计出菱形继承,像Java是不⽀持多继承的, 就避开了菱形继承。

不要去玩菱形继承!!!

3 虚继承

虚继承的关键字:virtual    virtual是为了解决菱形继承二叉性的问题

很多⼈说C++语法复杂,其实多继承就是⼀个体现。有了多继承,就存在菱形继承,有了菱形继承就有 菱形虚拟继承,底层实现就很复杂,性能也会有⼀些损失,所以最好不要设计出菱形继承。多继承可 以认为是C++的缺陷之⼀,后来的⼀些编程语⾔都没有多继承,如Java。

virtual要放在腰部的位置:

// 使⽤虚继承Person类 
class Student : virtual public Person
{
protected:int _num; //学号 
};

但是virtual不能多使用:virtual会影响底层的模型,到处都用就会导致底层一团乱

虚继承的底层太过于复杂,我们不讲解

4 io库中的菱形继承

但是在库里面,有实现菱形继承


四 继承与组合

1 继承和组合

继承和组合都是复用

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

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

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

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

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

软件设计是需要尽量低耦合,高内聚

优先使用组合:因为组合的关联度低(也就是低耦合),高内聚

低耦合的本质:一个地方有改动,不太会影响其他的地方

高耦合就是牵一发而动全身

2 示例

// Tire(轮胎)和Car(⻋)更符合has-a的关系 
class Tire {
protected:string _brand = "Michelin"; // 品牌 size_t _size = 17; // 尺⼨ 
};class Car {
protected:string _colour = "⽩⾊"; // 颜⾊ string _num = "陕ABIT00"; // ⻋牌号 Tire _t1; // 轮胎 Tire _t2; // 轮胎 Tire _t3; // 轮胎 Tire _t4; // 轮胎 
};class BMW : public Car {
public:void Drive() { cout << "好开-操控" << endl; }
};// Car和BMW/Benz更符合is-a的关系 
class Benz : public Car {
public:void Drive() { cout << "好坐-舒适" << endl; }
};
template<class T>
class vector
{};// stack和vector的关系,既符合is-a,也符合has-a 
template<class T>
class stack : public vector<T>
{};template<class T>
class stack
{
public:vector<T> _v;
};int main()
{return 0;
}

3 白盒复用&&黑盒复用

白盒复用(White-box Reuse)

定义通过继承实现的复用,派生类可以直接访问基类的内部成员(如 protected 成员),相当于 “打开盒子” 看到并使用内部实现

优点

  1. 复用效率高:派生类可以直接继承基类的成员和方法,无需重新实现,减少代码量。
  2. 便于扩展:派生类可以通过重写基类方法,灵活修改或扩展基类的行为。
  3. 紧密的逻辑关联:适用于基类与派生类存在明确 “is-a” 关系的场景(如 “学生是一种人”),逻辑清晰。

缺点

  1. 耦合度高:派生类与基类强绑定,基类的内部实现修改可能直接影响派生类,破坏封装性。
  2. 灵活性差:继承关系是静态的,编译时确定,运行时无法动态改变继承的基类。
  3. 可维护性低:过度使用继承会导致类层次复杂(如菱形继承问题),难以理解和维护。
黑盒复用(Black-box Reuse)

定义通过组合(或聚合)实现的复用,一个类将另一个类的对象作为成员,仅通过其公开接口访问,无需了解内部实现,相当于 “关闭盒子” 只使用外部功能

优点

  1. 低耦合:被复用的类(成员对象)的内部实现对使用者透明,修改其内部不影响使用者,封装性好。
  2. 灵活性高:可以在运行时动态替换成员对象(通过接口多态),适应不同场景。
  3. 避免继承缺陷:无需处理复杂的类层次,规避菱形继承的数据冗余和二义性问题。
  4. 可维护性强:类之间职责清晰,单个类的修改影响范围小。

缺点

  1. 代码量可能增加:需要手动封装成员对象的接口(如转发方法),不像继承那样自动获得基类方法。
  2. 接口依赖:如果成员对象的接口变化,使用者可能需要调整,依赖于接口的稳定性。
  3. 不适用于 “is-a” 关系:更适合 “has-a” 关系(如 “汽车有发动机”),若强行用于 “is-a” 场景,逻辑会不自然。


五 实现一个不能被继承的类

1 方法

方法1:C++98方法:基类的构造函数私有,派生类的构造必须调用基类的构造函数,但基类的构造函数私有化后,派生类无法访问,也就不能调用,那么派生类就无法实例化出对象。

方法2:C++11新增了一个final关键字,用final修改基类,派生类就不能继承了。

// C++11的⽅法 
class Base final
{
public:void func5() { cout << "Base::func5" << endl; }
protected:int a = 1;
private:// C++98的⽅法 /*Base(){}*/
};
class Derive :public Base 
{void func4() { cout << "Derive::func4" << endl; }
protected:int b = 2;
};
int main()
{Base b;Derive d;return 0;
}

2 例题

这是一个多继承的问题

p3指向的是整个对象  Base1在Base2前面是因为,先继承的在前面,后继承的在后面

http://www.dtcms.com/a/568658.html

相关文章:

  • css实战:常用伪元素选择器介绍
  • 4.4 路由算法与路由协议【2013统考真题】
  • 营销型网站建设需要备案吗上饶网站建设企业
  • 福建网站建设科技有限公司品牌建设还需持续力
  • 工业CMOS相机的原理及基础知识
  • 无人机电气隔离与抗干扰技术概述
  • Elasticsearch的学习
  • GitHub 热榜项目 - 日榜(2025-11-04)
  • SAP 概述
  • 深圳家具网站建设做网站需要会写代码6
  • 常见的网站文件后缀名
  • 18、docker-macvlan-2-示例
  • ICCV2025 | GLEAM:通过全局-局部变换增强的面向视觉-语言预训练模型的可迁移对抗性攻击
  • Visual Studio 编程工程设置
  • 自我系统更新
  • 【数据结构】双向链表的实现
  • 《Linux系统编程之开发工具》【版本控制器 + 调试器】
  • C++ :C宏函数的升级:内联函数inline
  • 青海网站建设费用织梦后台怎么建设网站
  • [特殊字符] Gudu SQL Omni 在数据治理体系中的落地实践指南
  • arm寄存器虚拟化分析
  • Linux网络传输层TCP协议
  • 做企业网站备案收费吗怎么修改网站标题
  • 机器视觉---Intel RealSense SDK 2.0 开发流程
  • 【AI基础篇】Transformer架构深度解析与前沿应用
  • QuickNacos
  • 用Python来学微积分30-微分方程初步
  • Opencv(七) : 图像颜色替换
  • Skywalking运维之路(Skywalking服务搭建)
  • 网站开发及建设赔偿条款中国最牛的十大企业