【C++】——继承(下)

news2024/11/25 4:53:24

【C++】——继承(下)

  • 5 继承与友元
  • 6 继承与静态成员
  • 7 多继承
    • 7.1 继承模型
    • 7.2 菱形继承的问题
    • 7.3 虚继承
    • 7.4 多继承中的指针偏移问题
  • 8 组合与继承

5 继承与友元

  友元关系不能被继承。即一个函数是父类的友元函数,但不是子类的友元函数。也就是说父类的友元不能访问子类的私有和保护成员

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;

	Display(p, s);
	return 0;
}

在这里插入图片描述

  这里出现了许多报错,但没关系,我们先看框出来的这一条
  这条报错说缺少",",一般出现这种报错,而我们检查出并没有","的相关问题时,通常都是 类型出问题了,一般是我们没有定义某个类型但我们直接去使用就会出现这种报错
  编译器遇到一个类型、变量、函数时都只会向上查找,这是为了提高编译速度
  出现报错的原因是friend void Display(const Person& p, const Student& s);中我们使用了 S t u d e n t Student Student 类型,它的定义在下面。但 S t u d e n t Student Student P e r s o n Person Person 的继承,又不可能将其放在 P e r s o n Person Person 的前面,我们可以在 P e r s o n Person Person 前加上 S t u d e n t Student Student前置声明

class Student;

  
  解决这个问题后报错就少很多啦,我们再看看剩下的报错

在这里插入图片描述

  这里就是友元函数的问题啦。 D i s p l a y ( ) Display() Display() P e r s o n Person Person 的友元,但友元关系不能继承下来,因此 D i s p l a y ( ) Display() Display() 不是 S t u d e n t Student Student 的友元。解决方法也很简单, S t u d e n t Student Student 中加一个有元声明就好

//前置声明
class Student;

class Person
{
public :
	friend void Display(const Person& p, const Student& s);
protected:
	string _name; // 姓名
};
class Student : public Person
{
public:
	friend void Display(const Person& p, const Student& s);
protected :
	int _stuNum; // 学号
};

void Display(const Person& p, const Student& s)
{
	cout << p._name << endl;
	cout << s._stuNum << endl;
}

  
  

6 继承与静态成员

  父类定义了 s t a t i c static static 静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个 s t a t i c static static 成员实例。
  而普通成员,假设父类有一个_name,子类继承下来也有另一个_name,但是他们两个_name不是同一个,各自是各自的。

我们来演示一下:

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;

	// 也可以通过对象进行访问
	cout << ++p._count << endl;
	cout << ++s._count << endl;
	
	return 0;
}

运行结果:

在这里插入图片描述

  虽然静态变量可以通过对象访问,但一般不这么做,大多数都是直接指定类域去访问

  
  

7 多继承

7.1 继承模型

  • 单继承一个子类只有一个直接父类时,称为这个继承关系为单继承

在这里插入图片描述

单继承

  

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

在这里插入图片描述

多继承

  

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

在这里插入图片描述

菱形继承

  

7.2 菱形继承的问题

  菱形继承是很坑的,有数据冗余(浪费空间)和二义性(不知访问哪个)的问题,现实中不想被打就不要设计出菱形继承(多继承是没问题的,不要搞出菱形继承就行)。

  我们通过代码来看一下菱形继承存在的问题

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; // 主修课程
};

  
  上述就是菱形继承, P e r s o n Person Person 成员在 A s s i s t a n t Assistant Assistant 对象中有两份。我们试着访问 P e r s o n Person Person 的成员 _ n a m e name name

int main()
{
	Assistant a;
	a._name = "peter";

	return 0;
}

在这里插入图片描述

  
  我们需要指定访问那个父类成员的成员可以解决二义性的问题,但是数据冗余问题无法解决

int main()
{
	Assistant a;
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";

	return 0;
}

在这里插入图片描述

虽然解决了二义性,但数据冗余问题无法解决,Person 有两份

  
  不仅如此,因为数据冗余,导致菱形继承对象的大小特别大

int main()
{
	Assistant a;
	cout << sizeof(Assistant) << endl;
	return 0;
}

在这里插入图片描述

  


  那么现实中有没有人设计出菱形继承呢?还真有,我们简单看一下

在这里插入图片描述

在这里插入图片描述

  虽然但是,别学他
  

7.3 虚继承

  为了解决菱形继承的问题,C++ 引入了虚继承的概念,新增关键字: v i r t u a l virtual virtual

class Person
{
public:
	string _name; // 姓名
};

class Student : virtual public Person
{
protected :
	int _num; //学号
};

class Teacher : virtual public Person
{
protected :
	int _id; // 职⼯编号
};

class Assistant : public Student, public Teacher
{
protected :
	string _majorCourse; // 主修课程
};

  注意:因为是Person有数据冗余和二义性,所以是 Student 和 Teacher 继承 Person 时是虚继承,加 virtual 关键字
  

int main()
{
	Assistant a;
	a._name = "peter";
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";

	return 0;
}

  加了虚继承后,就只有一份 P e r s o n Person Person 成员了,共用了。既可以直接访问也可以指定类域访问。

在这里插入图片描述

  这里虽然监视窗口显示的是 3 个 P e r s o n Person Person,到那实际上他们是共用的
  


  其实库中的菱形继承也是用虚继承来解决的

template<class CharT, class Traits = std::char_traits<CharT>>
class basic_ostream : virtual public std::basic_ios<CharT, Traits>
{};
template<class CharT, class Traits = std::char_traits<CharT>>
class basic_istream : virtual public std::basic_ios<CharT, Traits>
{};

  那虚继承对 S t u d e n t Student Student T e a c h e r Teacher Teacher 有什么影响吗?
  底层的角度有一些影响,用的角度没有影响。从用的角度来说 S t u d e n t Student Student T e a c h e r Teacher Teacher 就是一个单继承。 v i r t u a l virtual virtual 真正影响的是下面的 A s s i s t a n t Assistant Assistant

S t u d e n t Student Student T e a c h e r Teacher Teacher 都要给虚继承,不能只给其中一个虚继承


在这里插入图片描述

  那这样算不算菱形继承呢?
  算的,菱形继承并不是看是否构成菱形,而是看某个类是否被重复继承,是否产生数据冗余和二义性

  那如果我们要加虚继承,该加在哪里呢?
   B B B C C C。因为虚继承是:谁会产生数据冗余和二义性,谁继承它时就要虚继承。在 E E E 中是 A A A 有数据冗余二义性,所以 B B B C C C 继承 A A A 时使用虚继承

  那能不能全部加上虚继承呢? D D D E E E 都加上
  不要,毕竟是药三分毒。

  总结:单继承和多继承可以用,但使用多继承时不要设计出菱形继承

  
  

7.4 多继承中的指针偏移问题

  • 下面说法中正确的是()
    A:p1 == p2 == p2    B:p1 < p2 < p3    C:p1 == p3 != p2    D:p1 != p2 != p3    E:p2 == p3 != p1
class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base2, public Base1 { public: int _d; };
int main()
{
	Derive d;
	Base1* p1 = &d;
	Base2* p2 = &d;
	Derive* p3 = &d;
	return 0;
}

要做对这道题,我们要知道下面两个知识点:

  • 子类给给父类的对象/指针/引用,会发生切片;子类对象给子类指针,指向的是整个子类对象,子类对象给父类指针,指向的是子类对象中父类的那一部分
  • 多继承对象在内存中的声明是先继承的基类在前⾯,后⾯继承的基类在后⾯,派⽣类成员在放到最后⾯

在这里插入图片描述

  首先, p 3 p3 p3 指向开始肯定是没问题的。对 p 2 p2 p2将子类对象给父类指针,其会指向子类中父类的那一部分 p 2 p2 p2 指向 B a s e 2 Base2 Base2,因为 B a s e 2 Base2 Base2 先继承,所以 p 2 p2 p2指向开始 p 1 p1 p1 则指向子类对象 B a s e 1 Base1 Base1 的部分, p 1 p1 p1 不可能再指向开始,它发生了偏移。这题选 E

  
  

8 组合与继承

  • p u b l i c public public 继承是一种 i s is is- a a a 的关系。也就是说每个子类对象都一个父类对象
  • 组合 是一种 h a s has has- a a a 的关系。假设 B B B 组合了 A A A每个 B 对象都一个 A 对象

  什么意思呢?我举个例子大家就明白了

//组合
class Stack
{
public:
	//成员函数

private:
	vector<int> v;
};


//继承
class Stack : public vector<int>
{

};

我们再来看下 i s is is- a a a h a s has has- a a a

  • 组合是一种 h a s has has- a a a 的关系:栈一个数组
  • 继承是一种 i s is is- a a a 的关系:栈一个数组

  


  • 继承允许你根据父类的实现定义子类的实现。这种通过生成子类的复用通常称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言在继承方式中,父类的内部细节对子类可见。继承一定程度破坏了父类的封装,父类的改变,对子类有很大的影响。子类和父类间的依赖关系很强,耦合度很高
  • 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象类获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合之间没有很强的依赖关系,耦合度低优先使用对象组合有助于你保持每个类的封装
      

什么是黑什么是白呢?
测试中分为黑盒测试和白盒测试

  • 黑盒测试:指看不见里面的实现,也不需要看见你的实现。比如现在有一个新的 APP,我只需要从用户用的角度去测试。黑盒测试有些地方也叫功能测试
  • 白盒测试:白盒测试要了解其底层实现,从代码运行逻辑角度进行测试。
      
    白盒测试明显比黑盒测试更难

  那是耦合度高好还是耦合度低好呢?肯定是耦合度低好

  比如现在有两个模块,模块一有100个接口函数,且全部对模块二透明,那模块二就能使用模块一的任意多个函数接口来实现自己的功能。但如果模块一今天把这个函数的参数类型改了,明天吧那个函数的参数个数给改了,因为模块二是依赖模块一的,模块二也只能跟着改。
  但如果模块一虽然有100个函数接口,但只提供5个最关键函数接口给模块二。这时,两模块之间的耦合度就大大降低,只要模块一不改那5个函数,其他95个函数随便改都不影响模块二。

  所以软件工程中提出了一个低耦合、高内聚的概念。高内聚可以认为是一个模块里面关系越紧密越好, 没关系的就拿出去

  所以两个类的关系是继承好还是组合好呢?明显是组合更好,因为继承关系下,父类的任何改动都可能会影响子类
  


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

  
比如: T i r e Tire Tire(轮胎) 和 C a r Car Car(车) 更符合 h a s has has- a a 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; // 轮胎
};

  车 h a s has has- a a a 轮胎是正常的,但车 i s is is- a a a 轮胎就是错的

  但 C a r Car Car B M W BMW BMW / B e n z Benz Benz(宝马/奔驰)更符合 i s is is- a a a 的关系

class BMW : public Car 
{
public:
	void Drive() { cout << "好开-操控" << endl; }
};
// Car和BMW/Benz更符合is-a的关系
class Benz : public Car {
public:
	void Drive() { cout << "好坐-舒适" << endl; }
};

  只能说宝马/奔驰 i s is is- a a a 车,不能说宝马/奔驰 h a s has has- a a a
  

//组合
class Stack
{
public:
	//成员函数

private:
	vector<int> v;
};


//继承
class Stack : public vector<int>
{

};

  但既可以说:栈有一个数组,也可以说:栈是一个数组,这种情况下优先使用组合

  判断两个类型适合组合还是继承,就用 i s is is- a a a h a s has has- a a a 来判断

  
  
  
  


  好啦,本期关于 priority_queue 与仿函数 的知识就介绍到这里啦,希望本期博客能对你有所帮助。同时,如果有错误的地方请多多指正,让我们在 C++ 的学习路上一起进步!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2207868.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

独立站外链策略如何确保SEO效果最大化?

在SEO优化中&#xff0c;外链的建设是不可忽视的重要环节。特别是独立站外链&#xff0c;它不仅能够提升网站在搜索引擎中的排名&#xff0c;还能通过高质量的dofollow链接&#xff0c;促进谷歌对网站的快速收录。那么该如何建立一套有效的独立站外链策略&#xff1f; 首先&…

与C++内存管理和STL简介的爱恨情仇

本文 1.C/C内存分布2.C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free总结 3.C内存管理方式new/delete操作内置类型new和delete操作自定义类型 4.operator new与operator delete函数&#xff08;重要点进行讲解&#xff09;5.new和delete的实现原理内置类型自定…

Redis主从复制机制详解

目录 一、主从复制介绍二、搭建主从复制三、主从复制流程四、关于Replication ID五、主从复制核心知识六、主从复制应用场景七、主从复制的注意事项 一、主从复制介绍 1、什么是主从复制&#xff1f; 2、为什么要使用主从复制&#xff1f; redis-server单点故障。单节点QPS…

MyBatis XML映射文件

XML映射文件 XML映射文件的名称与Mapper接口名称一致&#xff0c;并且将XML映射文件和Mapper接口放置在相同包下&#xff08;同包同名&#xff09;XML映射文件的namespace属性为Mapper接口全限定名一致XML映射文件中SQL语句的id与Mapper接口中的方法名一致&#xff0c;并保持返…

MBI6665Q升降压LED驱动芯片车规级AEC-Q100

MBI6665Q是由聚积科技&#xff08;Macroblock Inc.&#xff09;开发的一款多拓扑恒流LED驱动器&#xff0c;主要用于汽车照明应用。凭借其强大的功能集&#xff0c;MBI6665Q可以满足高效照明解决方案的需求&#xff0c;广泛应用于日间行车灯&#xff08;DRL&#xff09;、雾灯等…

idea的maven组件管理依赖小规则

pom文件引入一个依赖&#xff0c;idea会先找到依赖&#xff0c;然后才更新界面&#xff0c;如果找不到&#xff0c;不会更新界面&#xff0c;除非指定正确的版本才会更新界面&#xff0c;更新界面后&#xff0c;再次指定一个错误的版本&#xff0c;idea不会更新界面&#xff0c…

海康大华等厂家摄像头、执法记录仪等通过GB28181注册到LiveGBS平台,如何实时获取设备和通道的在线状态

LiveGBS如何订阅设备状态在线离线状态redis订阅设备或是通道状态subscribe device操作及示例 1、如何监听设备状态2、device订阅2.1、设备上线消息2.2、设备离线消息2.2、通道上线消息2.2、通道离线消息 3、订阅示例3.1、连接REDIS3.2、订阅device示例3.3、设备上线示例3.3.1、…

2014年国赛高教杯数学建模B题创意平板折叠桌解题全过程文档及程序

2014年国赛高教杯数学建模 B题 创意平板折叠桌 某公司生产一种可折叠的桌子&#xff0c;桌面呈圆形&#xff0c;桌腿随着铰链的活动可以平摊成一张平板&#xff08;如图1-2所示&#xff09;。桌腿由若干根木条组成&#xff0c;分成两组&#xff0c;每组各用一根钢筋将木条连接…

【SpringBoot详细教程】-13-SpringBoot整合事务管理 【持续更新】

Hello&#xff0c;大胸弟们&#xff0c;我们又又又见面了&#xff0c;今天攀哥继续为大家分享一下SpringBoot的教程&#xff0c;没点关注的宝宝&#xff0c;点一下关注。 &#x1f332; 事务管理的意义 &#x1f33f; 身边事务的案例&#xff1a; 桃子向好友攀哥汇款5000元…

STL之set、map的使用

STL之set、map 1. 序列式容器和关联式容器2. set系列的使⽤参考文档链接&#xff1a;2.1 set的介绍&#xff08;2&#xff09;set的增删查2.2 multiset的介绍 3 map3.1 参考文档3.2 map类的介绍3.3 pair类型介绍3.4 map的构造3.6 map的数据修改3.7 multimap和map的差异 1. 序列…

鸿蒙next 电商实战项目 来了

前言&#xff1a; 最新在学习鸿蒙next 开发 就写了一个demo 今天就分享给大家一下 效果图 客户端实现 1 底部导航器实现 import choice from ../view/ChoicePage import HomePage from ../view/HomePage import MyPage from ../view/MyPage import Shoppingcar from ../view…

【Canvas与诗词】要做一棵树,站成永恒

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>要做一棵树站成永恒</title><style type"text/css&quo…

电阻负载柜的工作原理是什么?

电阻负载柜是用于模拟电力系统中各种负载的设备&#xff0c;它可以模拟实际负载的各种特性&#xff0c;如阻性、感性和容性负载。电阻负载柜在电力系统、电气设备测试和维护等领域具有广泛的应用。其工作原理主要包括以下几个方面&#xff1a; 电阻负载柜的核心部分是电阻元件…

根据请求错误的状态码判断代理配置问题

SafeLine&#xff0c;中文名 “雷池”&#xff0c;是一款简单好用, 效果突出的 Web 应用防火墙(WAF)&#xff0c;可以保护 Web 服务不受黑客攻击。 雷池通过过滤和监控 Web 应用与互联网之间的 HTTP 流量来保护 Web 服务。可以保护 Web 服务免受 SQL 注入、XSS、 代码注入、命…

后端——eclipse实现前端后端的交互(1)

1.创建Web Project 首先&#xff0c;为实现前端后端交互&#xff0c;要将HTML文件和Java后端文件放入eclipse。 右键—>New—>Project—>选择“Dynamic Web Project” 创建完成 这里我们会看到报了一个错&#xff1a; Description Resource Path Location Typ…

GC1262E替代APX9262S/茂达芯片在笔记本和显卡风散热风扇中的应用分享

随着移动计算和高性能图形处理技术的不断进步&#xff0c;笔记本电脑和显卡的散热需求日益增加。散热风扇作为关键组件&#xff0c;其控制芯片的选择对系统性能和用户体验有着直接影响。本文将探讨芯麦的GC1262E芯片如何替代APX9262S/茂达芯片&#xff0c;应用于笔记本和显卡的…

持续研发赋能,强达电路具备多项核心技术

深圳市强达电路股份有限公司&#xff08;以下简称&#xff1a;强达电路或公司&#xff09;深耕PCB行业二十年&#xff0c;主营业务为PCB的研发、生产和销售&#xff0c;是一家主要专注于中高端样板和小批量板产品的PCB企业。公司2004年创立于深圳&#xff0c;抓住国内早期缺少中…

面试宝典:揭秘50个大型模型核心问题精选

我精选50个大模型高频面试题&#xff0c;分享给大家 简述GPT和BERT的区别讲一下GPT系列模型是如何演进的&#xff1f;为什么现在的大模型大多是decoder-only的架构&#xff1f;讲一下生成式语言模型的工作机理哪些因素会导致LLM的偏见&#xff1f;LLM中的因果语言建模与掩码语…

【问题实战】Jmeter中jtl格式转换图片后如何分开展示各个性能指标?

【问题实战】Jmeter中jtl格式转换图片后如何分开展示各个性能指标&#xff1f; 遇到的问题解决方法查看修改效果 遇到的问题 JMeter测试计划中只设置了一个性能监控器jpgc - PerfMon Metrics Collector&#xff1b;在这个监控器中设置几个性能监控指标&#xff0c;比如CPU、Di…

电脑快速切换IP地址命令是什么?详解与实践

有时&#xff0c;出于安全考虑或测试需要&#xff0c;我们可能需要快速切换电脑的IP地址。虽然这一过程在初学者看来可能略显复杂&#xff0c;但通过简单的命令和步骤&#xff0c; 即使是普通用户也能轻松实现。本文将详细介绍在Windows系统中快速切换IP地址的几种方法&#xf…