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

【C++ 学习 ⑰】- 继承(下)

目录

一、派生类的默认成员函数

二、继承与友元

三、继承与静态成员

四、复杂的菱形继承及菱形虚拟继承

五、继承和组合


 

 


一、派生类的默认成员函数

  1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认构造函数,那么必须在派生类的构造函数的初始化列表中显示调用基类的构造函数。

  2. 派生类的拷贝构造函数必须调用基类的拷贝构造函数完成基类的那一部分成员的拷贝初始化。

  3. 派生类的 operator= 必须调用基类的 operator= 完成基类的那一部分成员的赋值。

  4. 派生类的析构函数在被调用完后,会自动调用基类的析构函数清理基类的那一部分成员,即在派生类的析构函数中不用显示地调用基类的析构函数。

  5. 在创建派生类对象时,先调用基类的构造函数,再调用派生类的构造函数。

  6. 在销毁派生类对象时,先调用派生类的析构函数,再调用基类的析构函数。

#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;
}

f0745668420b4395a7ba770acb62a36c.png

 

为了解决菱形继承中的问题,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;
}

5d814df4b34a4815b1592c104b9111da.png

 

虚继承底层实现原理与编译器相关,一般是通过虚基类指针和虚基类表实现的

每个虚继承的子类都有一个虚基类指针,该指针指向一个虚基类表,表中记录了虚基类和本类的偏移量,通过这个偏移量就可以找到虚基类成员

当虚继承的子类被当作父类继承时,虚基类指针也会被继承

 

 


五、继承和组合

面向对象系统中功能复用的两种最常用技术是类继承对象组合(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++ 学习 ⑰】- 继承(下)

目录 一、派生类的默认成员函数 二、继承与友元 三、继承与静态成员 四、复杂的菱形继承及菱形虚拟继承 五、继承和组合 一、派生类的默认成员函数 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认构造函数&#xff0c;那么必须在派生…...

kafka学习笔记

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

阀门状态监测和预测性维护的原理和实施步骤

随着制造业数字化转型的推进&#xff0c;预测性维护&#xff08;Predictive Maintenance&#xff0c;简称PdM&#xff09;成为提高生产效率和设备可靠性的关键策略之一。在流程工厂中&#xff0c;阀门作为重要的设备之一&#xff0c;起着控制流体流动的关键作用。本文将探讨如何…...

复习之web服务器--apache

PS&#xff1a;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多线程&#xff08;三&#xff09; 文章目录 Linux知识点 -- Linux多线程&#xff08;三&#xff09;一、线程同步1.概念理解2.条件变量3.使用条件变量进行线程同步 二、生产者消费者模型1.概念2.基于BlockingQueue的生产者消费者模型3.单生产者单消费者模…...

android java 硬编码保存mp4 jni数据转换

目录 java imagereader编码保存 java NV21toYUV420SemiPlanar 编码保存视频用&#xff1a; imageReader获取nv21 jni NV12toYUV420SemiPlanar函数&#xff1a; 代码来自博客&#xff1a; 【Android Camera2】彻底弄清图像数据YUV420_888转NV21问题/良心教学/避坑必读!_yuv…...

那些你不得不知道的HTML知识点

目录 1、行内元素有哪些&#xff1f;块级元素有哪些&#xff1f; 空(void)元素有哪些&#xff1f;2、页面导入样式时&#xff0c;使用link和import有什么区别&#xff1f;3、title与h1的区别、b与strong的区别、i与em的区别&#xff1f;3.1 title与h1的区别&#xff1a;3.2 b与…...

如何复制主播的性格(此乃广告文)

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

【ES6】—【新特性】—Symbol详情

一、一种新的原始数据类型 定义&#xff1a;独一无二的字符串 二、 声明方式 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 平台&#xff0c;其内部集成了大量精良的 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&#xff0c;它是一种强大的工具&#xff0c;能够有效地编写和执行单元测试。unittest 提供了完整的测试结构&#xff0c;支持自动化测试的执行&#xff0c;能够对测试用例进行组织&#xff0c;并且提供了丰富的断言方法。最终…...

mysql 获取json数组中某个字段根据下标

MySQL获取JSON数组中某个字段根据下标 在MySQL中&#xff0c;JSON数据类型可以方便地存储、操作和查询包含复杂结构的数据。当我们需要从JSON数组中获取某个字段时&#xff0c;可以使用MySQL的JSON函数来实现。 1. JSON数据类型简介 JSON&#xff08;JavaScript Object Nota…...

深入理解Redis缓存穿透、击穿、雪崩及解决方案

深入理解Redis缓存穿透、击穿、雪崩及解决方案 一、简介Redis 简介缓存作用与优化 二、缓存问题的分类缓存穿透缓存击穿缓存雪崩 三、缓存穿透的解决方案布隆过滤器缓存空对象接口层校验 四、缓存击穿的解决方案互斥锁热点数据提前加载 五、缓存雪崩的解决方案增加缓存容错能力…...

java八股文面试[java基础]——字节码

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

新能源汽车技术的最新进展和未来趋势

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

知虾shopee数据分析工具:shopee出单的商机利器

当今数字化时代&#xff0c;数据已经成为商业成功的关键要素之一。而Shopee作为东南亚最大的电商平台之一&#xff0c;其强大的数据分析工具正为商家提供了宝贵的市场洞察和决策支持。本文将深入探讨Shopee数据分析工具如何帮助商家抓住商机并取得成功。 洞察消费者需求&#x…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...