【C++ 学习 ⑰】- 继承(下)
目录
一、派生类的默认成员函数
二、继承与友元
三、继承与静态成员
四、复杂的菱形继承及菱形虚拟继承
五、继承和组合
一、派生类的默认成员函数
-
派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认构造函数,那么必须在派生类的构造函数的初始化列表中显示调用基类的构造函数。
-
派生类的拷贝构造函数必须调用基类的拷贝构造函数完成基类的那一部分成员的拷贝初始化。
-
派生类的 operator= 必须调用基类的 operator= 完成基类的那一部分成员的赋值。
-
派生类的析构函数在被调用完后,会自动调用基类的析构函数清理基类的那一部分成员,即在派生类的析构函数中不用显示地调用基类的析构函数。
-
在创建派生类对象时,先调用基类的构造函数,再调用派生类的构造函数。
-
在销毁派生类对象时,先调用派生类的析构函数,再调用基类的析构函数。
#include <iostream>
using namespace std;
class Person
{
public:Person(const char* name = "张三", int age = 18): _name(name), _age(age){cout << "Person::default constructor" << endl;}
Person(const Person& p): _name(p._name), _age(p._age){cout << "Person(const Person& p)" << endl;}
Person& operator=(const Person& p){cout << "Person& operator=(const Person p)" << endl;if (this != &p){_name = p._name;_age = p._age;}return *this;}
~Person(){cout << "~Person()" << endl;}
protected:string _name; // 姓名int _age; // 年龄
};
class Student : public Person
{
public:Student(const char* name = "张三", int age = 18, int stu_id = 0): Person(name, age), _stu_id(stu_id){cout << "Student::default constructor" << endl;}
Student(const Student& s): Person(s), _stu_id(s._stu_id){cout << "Student(const Student& s)" << endl;}
Student& operator=(const Student& s){cout << "Student& operator=(const Student& s)" << endl;if (this != &s){Person::operator=(s); // operator=(s); 会陷入死循环_stu_id = s._stu_id;}return *this;}
~Student(){cout << "~Student()" << endl;}
protected:int _stu_id; // 学号
};
int main()
{Student s1("李四", 20, 1);// Person::default constructor// Student::default constructor
Student s2(s1);// Person(const Person& p)// Student(const Student & s)
Student s3;// Person::default constructor// Student::default constructor
s3 = s1;// Student& operator=(const Student& s)// Person& operator=(const Person p)
// ~Student()// ~Person()// ~Student()// ~Person()// ~Student()// ~Person()return 0;
}
二、继承与友元
友元关系不能继承,即基类友元不能访问子类私有和保护成员。
#include <iostream>
using namespace std;
class Student;
class Person
{friend void Print(const Person& p, const Student& s);
protected:string _name = "张三";int _age = 18;
};
class Student : public Person
{// 必须声明,否则会报错friend void Print(const Person& p, const Student& s);
protected:int _stu_id = 0;
};
void Print(const Person& p, const Student& s)
{cout << p._name << " " << p._age << endl;cout << s._stu_id << endl;
}
int main()
{Person p;Student s;Print(p, s);return 0;
}
三、继承与静态成员
如果基类定义了 static 静态成员,无论派生出了多少个类,在整个继承体系中都只有一个 static 静态成员实例。
#include <iostream>
using namespace std;
class Person
{
public:Person() { ++_count; }
protected:string _name;int _age;
public:static int _count; // static 静态成员变量
};
int Person::_count = 0;
class Student : public Person
{
protected:int _stu_id;
};
class Graduate : public Student
{
protected:string _seminarCourse; // 研究科目
};
int main()
{cout << &Person::_count << " " << &Student::_count<< " " << &Graduate::_count << endl;// 输出的三个地址相同Person p1;Student s1;Student s2;Graduate g1;Graduate g2;Graduate g3;cout << Person::_count << endl; // 6return 0;
}
四、复杂的菱形继承及菱形虚拟继承
单继承:一个派生类只有一个直接基类时,称这种继承关系为单继承。
多继承:一个派生类有两个或两个以上直接基类时,称这种继承关系为多继承。
菱形继承是多继承的一种特殊情况,它会造成数据冗余和二义性的问题,例如:
#include <iostream>
using namespace std;
// 间接基类 A
class A
{
public:int _a;
};
// 直接基类 B
class B : public A
{
public:int _b;
};
// 直接基类 C
class C : public A
{
public:int _c;
};
// 派生类 D
class D : public B, public C
{
public:int _d;
};
int main()
{D d;// 为了消除歧义,必须在 _a 前面指明它具体来自哪个类d.B::_a = 0;d.C::_a = 1;d._b = 2;d._c = 3;d._d = 4;return 0;
}
为了解决菱形继承中的问题,C++ 提出了虚继承,使得在派生类中只保留一份间接基类的成员。
在继承方式前面加上 virtual 关键字就是虚继承,例如:
#include <iostream>
using namespace std;
// 间接基类 A
class A
{
public:int _a;
};
// 直接基类 B
class B : virtual public A // 虚继承
{
public:int _b;
};
// 直接基类 C
class C : virtual public A // 虚继承
{
public:int _c;
};
// 派生类 D
class D : public B, public C
{
public:int _d;
};
int main()
{D d;d._a = 1; // okd._b = 2;d._c = 3;d._d = 4;return 0;
}
虚继承底层实现原理与编译器相关,一般是通过虚基类指针和虚基类表实现的。
每个虚继承的子类都有一个虚基类指针,该指针指向一个虚基类表,表中记录了虚基类和本类的偏移量,通过这个偏移量就可以找到虚基类成员。
当虚继承的子类被当作父类继承时,虚基类指针也会被继承。
五、继承和组合
面向对象系统中功能复用的两种最常用技术是类继承和对象组合(object composition)。
类继承允许你根据基类的实现定义派生类的实现,这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)。术语 "白箱" 是相对可视性而言的:在继承方式中,基类的内部细节对派生类可见。继承一定程度上破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类的依赖关系很强,耦合度高。
对象组合是类继承之外的另一种复用选择,新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(white-box reuse),因为对象的内部细节是不可见的,对象只能以 "黑箱" 的形式出现。组合类之间没有很强的依赖关系,耦合度低。
因此在实际中,尽量多使用组合。不过继承也有用武之地的:public 继承是一种 is-a 的关系,即每个派生类对象都是一个基类对象,组合是一种 has-a 的关系,即假设 B 组合了 A,那么每个 B 类对象都有一个 A 类对象,而有些关系就适合用继承;另外要实现多态,也必须要用继承。
例一:
class Car
{
protected:string _color; // 颜色string _num; // 车牌号
};
class AITO : public Car
{
public:void Describe() const { cout << "Intelligent" << endl; }
};
class AVATR : public Car
{
public:void Describe() const { cout << "luxurious" << endl; }
};
AITO、AVATR 和 Car 构成 is-a 的关系。
例二:
class Tire
{
protected:string _brand; // 品牌size_t _size; // 尺寸
};
class Car
{
protected:string _color;string _num;Tire _t;
};
Car 和 Tire 构成 has-a 的关系。
相关文章:

【C++ 学习 ⑰】- 继承(下)
目录 一、派生类的默认成员函数 二、继承与友元 三、继承与静态成员 四、复杂的菱形继承及菱形虚拟继承 五、继承和组合 一、派生类的默认成员函数 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认构造函数,那么必须在派生…...

kafka学习笔记
1、kafka是什么? kafka是一个高吞吐,分布式,基于发布/订阅的消息系统,最大的特性就是可以实时的处理大量的数据以满足各种需求场景:日志收集,离线和在线的消息消费,等等 2、kakfa的基础架构&am…...

阀门状态监测和预测性维护的原理和实施步骤
随着制造业数字化转型的推进,预测性维护(Predictive Maintenance,简称PdM)成为提高生产效率和设备可靠性的关键策略之一。在流程工厂中,阀门作为重要的设备之一,起着控制流体流动的关键作用。本文将探讨如何…...

复习之web服务器--apache
PS:Vim复制小技巧 一、实验环境 两台虚拟机 (nodea,nodeb)配置ip搭建软件仓库关闭selinux [rootftp Desktop]# hostnamectl set-hostname nodea.westos.org [rootftp Desktop]# hostname nodea.westos.org [rootftp Desktop]# ifconfig enp1s0: flags4163<UP,B…...
[Unity] 单例设计模式, 可供继承的单例组件模板类
一个可供继承的单例组件模板类: public class SingletonComponent<TComponent> : Componentwhere TComponent : SingletonComponent<TComponent> {static TComponent _instance;private static TComponent GetOrFindOrCreateComponent(){// 双检索if (_instance …...

Linux知识点 -- Linux多线程(三)
Linux知识点 – Linux多线程(三) 文章目录 Linux知识点 -- Linux多线程(三)一、线程同步1.概念理解2.条件变量3.使用条件变量进行线程同步 二、生产者消费者模型1.概念2.基于BlockingQueue的生产者消费者模型3.单生产者单消费者模…...
android java 硬编码保存mp4 jni数据转换
目录 java imagereader编码保存 java NV21toYUV420SemiPlanar 编码保存视频用: imageReader获取nv21 jni NV12toYUV420SemiPlanar函数: 代码来自博客: 【Android Camera2】彻底弄清图像数据YUV420_888转NV21问题/良心教学/避坑必读!_yuv…...
那些你不得不知道的HTML知识点
目录 1、行内元素有哪些?块级元素有哪些? 空(void)元素有哪些?2、页面导入样式时,使用link和import有什么区别?3、title与h1的区别、b与strong的区别、i与em的区别?3.1 title与h1的区别:3.2 b与…...

如何复制主播的性格(此乃广告文)
上面这份ppt写于Fay开源之前。当然,以现在的认知再去评价当时的设计,会发现有诸多的不严谨,甚至缺憾。比如,以单层的网络结构肯定无法拟合人性这个复杂的东西,人性也不是只受已知的几个参数所作用。但我现在想说的是&a…...

【ES6】—【新特性】—Symbol详情
一、一种新的原始数据类型 定义:独一无二的字符串 二、 声明方式 1. 无描述声明 let s1 Symbol() let s2 Symbol() console.log(s1, s2) // Symbol() Symbol() console.log(s1 s2) // falsePS: Symbol 声明的值是独一无二的 2. 有描述的声明 let s1 Symb…...

openresty安装与网站发布
文章目录 安装依赖下载安装包解压安装包安装启动nginx配置环境变量配置开机启动发布静态网站 OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动…...

创建延时队列、springboot配置多个rabbitmq
创建延时队列 queue.file_delay_destroy x-dead-letter-exchange: exchange.file_delay_destroy x-message-ttl: 259200000 259200000为3天,1000为1秒创建普通队列 queue.file_destroy创建普通交换机 exchange.file_delay_destroytype选择fanout 交换机绑定普通队列 (图中…...

在kaggle中用GPU使用CGAN生成指定mnist手写数字
文章目录 1项目介绍2参考文章3代码的实现过程及对代码的详细解析独热编码定义生成器定义判别器打印我们的引导信息模型训练迭代过程中生成的图片损失函数的变化 4总结5 模型相关的文件 1项目介绍 在GAN的基础上进行有条件的引导生成图片cgan 2参考文章 GAN实战之Pytorch 使用…...
【NI USRP】哪些 USRP 设备支持全双工,哪些支持半双工?
译者 东枫电子科技 设备构成 NI USRPEttus USRPUSRP-2900B200USRP-2901B210USRP-2920N210 WBXUSRP-2921N210 XCVR 2450USRP-2922N210 SBXUSRP-2930N210 WBX GPSDOUSRP-2932N210 SBX GPSDOUSRP-2940RX310 WBX (x2)USRP-2942RX310 SBX (x2)USRP-2943RX310 CBX (x2)U…...

不拼花哨,只拼实用:unittest指南,干货为王!
Python为开发者提供了内置的单元测试框架 unittest,它是一种强大的工具,能够有效地编写和执行单元测试。unittest 提供了完整的测试结构,支持自动化测试的执行,能够对测试用例进行组织,并且提供了丰富的断言方法。最终…...
mysql 获取json数组中某个字段根据下标
MySQL获取JSON数组中某个字段根据下标 在MySQL中,JSON数据类型可以方便地存储、操作和查询包含复杂结构的数据。当我们需要从JSON数组中获取某个字段时,可以使用MySQL的JSON函数来实现。 1. JSON数据类型简介 JSON(JavaScript Object Nota…...
深入理解Redis缓存穿透、击穿、雪崩及解决方案
深入理解Redis缓存穿透、击穿、雪崩及解决方案 一、简介Redis 简介缓存作用与优化 二、缓存问题的分类缓存穿透缓存击穿缓存雪崩 三、缓存穿透的解决方案布隆过滤器缓存空对象接口层校验 四、缓存击穿的解决方案互斥锁热点数据提前加载 五、缓存雪崩的解决方案增加缓存容错能力…...

java八股文面试[java基础]——字节码
字节码技术应用 字节码技术的应用场景包括但不限于AOP,动态生成代码,接下来讲一下字节码技术相关的第三方类库,第三方框架的讲解是为了帮助大家了解字节码技术的应用方向,文档并没有对框架机制进行详细分析,有兴趣的可…...

新能源汽车技术的最新进展和未来趋势
文章目录 电池技术的进步智能驾驶与自动驾驶技术充电基础设施建设新能源汽车共享和智能交通未来趋势展望结论 🎉欢迎来到AIGC人工智能专栏~探索新能源汽车技术的最新进展和未来趋势 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒🍹✨博客主页:IT陈寒的博客…...

知虾shopee数据分析工具:shopee出单的商机利器
当今数字化时代,数据已经成为商业成功的关键要素之一。而Shopee作为东南亚最大的电商平台之一,其强大的数据分析工具正为商家提供了宝贵的市场洞察和决策支持。本文将深入探讨Shopee数据分析工具如何帮助商家抓住商机并取得成功。 洞察消费者需求&#x…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...

C++实现分布式网络通信框架RPC(2)——rpc发布端
有了上篇文章的项目的基本知识的了解,现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...