《Effective C++中文版,第三版》读书笔记6
条款32:确定你的public继承塑模出is-a关系
简单知识点回顾(若不知道那就是扫盲了):
is-a关系:子类public继承父类。比如说Apublic继承了B。我们可以说A是B的一种特殊情况
has-a关系:指的是一种组合关系,是关联关系中的一种(一个类中有另一个类的实例),是整体和部分的关系。
更通俗点讲:其实就是这两种关系的字面意思。不用特别记忆。
真理:代码编译通过并不代表就可以正确运行
“public继承”意味着is-a
适用于base classes身上的每一件事情也一定适用于derived classed身上。
是不是可以这么理解:
我们在编写base classes时,应该让其更普遍一点。
条款33:避免遮掩继承而来的名字
derived classes 内的名称会遮掩base classes内的名称
为了让被遮掩的名称再见天日,可以适用using声明式或转交函数
using声明式会令继承而来的某个给定名称的所有同名函数在derived class都可见
何时使用这两种方式?
using声明式子
如果你继承base class并加上重载函数,而你又希望重新定义或覆写(推翻)其中的一部分,那么就需要为那些原本被遮掩的每个名称引入一个using声明式子。
转交函数:
有时候并不想继承(不能是public继承)base classes的所有函数,只想继承某一个版本的时候。就可以些一个转交函数,在函数内以"base class名称::函数名"的方式。
并不是所有编译器都支持using式子,这个时候可以考虑使用转交函数
条款34:区分接口继承和实现继承
public继承由两部分组成:函数接口继承和函数实现继承
对于基类中的接口,子类往往可能有三个方面的考虑;
1.子类只希望继承基类成员函数的接口 (基类中的纯虚函数)
2.子类希望同时继承基类中的接口和实现,还希望能够覆写。(基类中的虚函数(非纯虚))
3.子类希望同时继承函数的接口和实现,并且不允许覆写任何东西。(基类中的non-virtual函数)
类的设计者常犯的两个错误:
1.基类中的所有函数都声明为non-virtual
2.基类中的所有成员函数都声明为virtual(构造函数除外)
有些时候使用非纯的虚函数定义的接口,子类可能忘记实现,子类特异性的行为可能用基类的函数不适用:
这个时候可以考虑带定义纯虚函数,这样这个纯虚函数必须在子类中实现,子类无法忘记。
写一段体会一下(完善书中的例子)
class Shape
{
public:// 纯虚函数不能将其定义写在类内:pure-specifier on function-definition// 子类若想被实例化,那么继承了Shape,它就必须实现draw1,drawvirtual void draw() const = 0;// 这个纯虚函数,在基类这里不提供定义virtual void draw1() const = 0;// 非纯虚函数的目的是为了让子类继承该函数的接口和缺省实现virtual void error(const std::string &msg){std::cout << "Shape error msg = " << msg << std::endl;}int objectID() const{std::cout << "Hello world" << std::endl;return 0;}// 基类必须有一个虚的析构函数virtual ~Shape() = default;
};// 可以在类外实现纯虚函数的
void Shape::draw() const
{std::cout << "base pure virtual draw" << std::endl;
}class Rectangle : public Shape
{
public:Rectangle() {}~Rectangle() = default;// 继承自抽象class的子类,应该定义抽象基类中的纯虚函数,不定义的话,那么这个子类也是抽象class,无法实例化。virtual void draw() const override{std::cout << "Rectangls draw" << std::endl;}virtual void draw1() const override{std::cout << "Rectangls draw1" << std::endl;}
};class Ellipse : public Shape
{
public:Ellipse() {}~Ellipse() = default;virtual void draw() const override{std::cout << "Ellipse draw" << std::endl;}virtual void draw1() const override{std::cout << "Ellipse draw1" << std::endl;}virtual void error(const std::string &msg) override{std::cout << "Ellipse error msg = "<< msg << std::endl;}
};int main(int argc, char const *argv[])
{Rectangle test;Ellipse ellipse;test.draw();// 有定义的纯虚函数,可以通过这样的方式调用test.Shape::draw();// 非纯虚函数// 个人理解的缺省实现,意思就是你要实现就实现,不实现就算了。// Rectangle 中没有实现自己的error,所以实际调用的是Shape的errortest.error(std::string("XXXXXXXXXX"));// Ellipse中实现了自己的error, 这样调用实际上调的是它自己的errorellipse.error(std::string("XXXXXXXXXX"));// 可以这样调用基类中的函数ellipse.Shape::error(std::string("XXXXXXXXXX"));// non-virtual 意味着基类不希望这个操作因为子类的不同而不同。test.objectID();ellipse.objectID();return 0;
}
条款35:考虑virtual函数以外的其它选择
这个条款涉及到了设计模式,这个相关内容是我的弱项,但是已经纳入了强化学习内容。后续会出设计模式的学习
条款36:绝不重新定义继承而来的non-virtual函数
原因:
1.non-virtual 不是动态绑定的,无法通过基类指针访问子类的non-virtual函数,这样只能访问到基类的vritual
2.public继承是is-a的,但是重定义non-virtual后,通过子类访问non_virtual的表现出来的行为已经和基类的non_virtual不一样了。
写一段:
#include <iostream>class Base
{
public:void non_virtual(){std::cout << "It is Base non_virtual" << std::endl;}virtual void virtual1() const = 0;virtual ~Base() = default;
};// 要想为纯虚函数添加定义,只能在类外,类内会报错。
void Base::virtual1() const
{std::cout << "It is Base virtual1" << std::endl;
}class sun1 : public Base
{
public:sun1() {}~sun1() = default;void non_virtual(){std::cout << "It is sun1 non_virtual" << std::endl;}virtual void virtual1() const{std::cout << "It is sun1 virtual1" << std::endl;}
};int main(int argc, char const *argv[])
{Base *bp1 = new sun1;bp1->non_virtual();bp1->virtual1();// 按照public is-a 的这种思维,我们通过sun11访问non_virtual()的行为应该与基类中的一致// 但是实际上访问的是sun1自己的non_virtual(),那就违反了is-a这种关系了。sun1 sun11;sun11.non_virtual();// sun11.Base::non_virtual();return 0;
}// 输出结果
// It is Base non_virtual
// It is sun1 virtual1
// It is sun1 non_virtual
条款37:绝不重新定义继承而来的缺省参数值
静态类型:
在程序中被声明时所采用的类型
动态类型:
在运行的时候才能确定其类型
静态绑定:(前期绑定 early bingding)
在声明时就已经确定了其运行期间的样子。
动态绑定:(后期绑定,late binding)
在运行的时候才决定其真实的类型。
知识点:
virtual函数是动态绑定的,但是其缺省参数是静态绑定的。
为什么C++坚持virtual函数是动态绑定,但是缺省参数值是静态绑定呢?
运行期效率。为了程序的执行速度和编译器实现上的简易度。
请记住:
绝对不要重新定义一个继承而来的缺省参数值。
写一段:
#include <iostream>
#include <string>// 完善例子部分
class Shape
{
public:enum ShapeColor{Red,Green,Blue};void draw1(ShapeColor color = Red) const{doDraw(color);}//为什么要定义成纯虚函数? 目的是为了让子类必须实现,不实现无法实例化。防止忘了。virtual void draw(ShapeColor color = Red) const = 0;virtual ~Shape() = default;private:virtual void doDraw(ShapeColor color) const = 0;
};class Rectangle : public Shape
{
public:Rectangle() {}~Rectangle() = default;virtual void draw(ShapeColor color = Green) const{std::cout << "Rectangle draw color = " << color << std::endl;}private:virtual void doDraw(ShapeColor color) const{std::cout << "Rectangle doDraw color = " << color << std::endl;}
};class Circle : public Shape
{
public:Circle() {}~Circle() = default;virtual void draw(ShapeColor color) const{std::cout << "Circle draw color = " << color << std::endl;}private:virtual void doDraw(ShapeColor color) const{std::cout << "Circle doDraw color = " << color << std::endl;}
};void drawFunc(Shape *sp)
{sp->draw();
}void drawFunc2(Shape *sp)
{sp->draw1();
}int main(int argc, char const *argv[])
{Rectangle rectangle, *rp = &rectangle;Circle circle, *rc = &circle;// 缺省参数是静态绑定的,所以这样定义绑定的静态参数就是自己的rectangle.draw();circle.draw(circle.Green);// 导致有问题的用法drawFunc(rp);drawFunc(rc);// 替代方法: 将带有默认参数的定义为non-virtual, 然后调用一个私有的纯虚函数不带默认参数,子类覆写这个私有的virtual就行。drawFunc2(rp);drawFunc2(rc);// 是不是可以将所有的draw()的默认参数都改成red是不是就行了?// 但是如果某一天修改了基类的draw()的默认参数,那么所有子类都要跟着改。不修改的话一样的存在“重复定义一个继承而来的缺省参数值”问题return 0;
}
条款38:通过复合塑模出has-a或‘根据某物实现出’
复合是类型之间的一种关系,当某种类型的对象内含有它种类型的对象。两种类型之间就是has-a关系
请记住:
复合的意义和public继承完全不同
在应用域,复合意味has-a.在实现域,复合意味着根据某物实现出
条款39:明智而审慎地适用private继承
两条规则:
如果class之间的继承关系是private的那么编译器不会自动的将子类对象转换成父类对象。
通过private继承的而来的所有成员,不管其在父类中是什么属性,在子类中都会变成private属性。
private继承意味着子类对象根据基类对象实现而得。(这点和复合类型(has-a)很想)
如果可在复合类型(has-a)和private二者之间选择,那么优先选择复合
什么情况下使用private继承?
当子类想要访问基类的protect成分,或者为了重新定义一个或多个virtual函数
基类不含任何数据。没有non-static成员变量,没有virtual函数,没有virtual base class
小知识点:
C++裁定凡是独立(非附属)对象都必须有非零大小的空间。
标题解释:在考虑所有其它方案之后,如果仍然认为private继承是最佳办法,这是才使用private
请记住:
private继承意味着根据某物实现出。它通常比复合的级别低。如果子类要访问基类的protect成员。或者要重新定义继承而来的virtual函数时,这么设计时合理的
private继承可以造成empty base最优化。这个对于致力于“对象尺寸最小化”的程序库开发人员而言,可能很重要。
条款40:明智而审慎地适用多继承
请记住:
多重继承比单一继承复杂。他可能导致歧义性,以及对virtual继承的需要
virtual继承会增加大小、速度、初始化(及赋值)复杂度等等成本。如果virtual base classes 不带任何数据,将是最具实用价值的情况。
多重继承的确有正当用途。 其中一个情节涉及“public继承某个interface class”和 “private继承某个协助实现class”的两相组合
相关文章:

《Effective C++中文版,第三版》读书笔记6
条款32:确定你的public继承塑模出is-a关系 简单知识点回顾(若不知道那就是扫盲了): is-a关系:子类public继承父类。比如说Apublic继承了B。我们可以说A是B的一种特殊情况 has-a关系:指的是一种组合关系&…...

【Docker 】Docker 客户端,容器使用,启动容器,启动已停止运行的容器,停止一个容器,进入容器
作者简介: 辭七七,目前大一,正在学习C/C,Java,Python等 作者主页: 七七的个人主页 文章收录专栏: 七七的闲谈 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖…...

CTFshow 菜狗杯 web方向 全
文章目录 菜狗杯 web签到菜狗杯 web2 c0me_t0_s1gn菜狗杯 我的眼里只有$菜狗杯 抽老婆菜狗杯 一言既出菜狗杯 驷马难追菜狗杯 TapTapTap菜狗杯 Webshell菜狗杯 化零为整菜狗杯 无一幸免菜狗杯 无一幸免_FIXED菜狗杯 传说之下(雾)菜狗杯 算力超群菜狗杯 算…...

深入理解sql:进阶版
目录 背景举例子查询和嵌套查询:联合查询(UNION和UNION ALL):窗口函数:CTE(公共表达式):索引优化:事务隔离级别和锁定:性能优化:存储过程和函数&a…...

day31 | 455.分发饼干、376. 摆动序列、53. 最大子序和
目录: 解题及思路学习 455. 分发饼干 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。 对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸&#…...

C# textBox 右键菜单 contextMenuStrip
需求: 想在上图空白处可以右键弹出菜单,该怎么做呢? 1.首先,拖出一个 ContextMenuStrip。 随便放哪里都行,如下: 2.在textBox里关联这个“右键控件”即可,如下: 最终效果如下: 以上…...

TCP拥塞控制详解 | 7. 超越TCP
网络传输问题本质上是对网络资源的共享和复用问题,因此拥塞控制是网络工程领域的核心问题之一,并且随着互联网和数据中心流量的爆炸式增长,相关算法和机制出现了很多创新,本系列是免费电子书《TCP Congestion Control: A Systems …...

stm32之26.spi外设
...

C++信息学奥赛1177:奇数单增序列
#include<bits/stdc.h> using namespace std; int main(){int n;cin>>n; // 输入整数 n,表示数组的大小int arr[n]; // 创建大小为 n 的整型数组for(int i0;i<n;i) cin>>arr[i]; // 输入数组元素for(int i0;i<n;i){ // 对数组进行冒泡排序f…...

Java的数组是啥?
1.数组是啥? 数组是一块连续的内存,用来存储相同类型的数据 (1)如何定义数组? 1.int[] array {1,2,3,4} new int[]{1,2,3,4};//这里的new是一个关键字,用来创建对象 2.数组就是一个对象 动态初始化 …...

我的私人笔记(安装hadoop)
1.安装hadoop01环境 注需安装最小安装和使用英文界面 2.安装群集 // 获得网关IP:192.168.80.2 获得子网掩码:255.255.255.0 // 获得网段:[起始IP地址]192.168.128 --- [结束IP地址]192.168.80.254 // 计划集群的ip和主机名 //192.168.80.…...

【板栗糖GIS】——360浏览器的下载图标隐藏在内部不方便,怎么修改
目录 1. 设置前的本来样子 2. 登录360的皮肤中心 3. 使用se13的经典皮肤 最近edge浏览器最近使用bilibili和notion都非常卡,时不时崩溃,不得不换浏览器使用,试来试去360浏览器最得我心,只不过广告太多,调教也是花了…...

SpringMVC之文件上传和下载
文章目录 前言一、文件下载二、文件上传总结 前言 实现下载文件和上传文件的功能。 一、文件下载 使用ResponseEntity实现下载文件的功能 RequestMapping("/testDown") public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOEx…...

简单了解OSI网络模型
目录 一、协议是什么? 二、OSI七层模型 三、TCP/IP五层模型 一、协议是什么? 协议顾名思义就是通过大家伙一起协商讨论达成的统一规则和标准。网络协议就是规定用户数据信息如何在网络上传播以及实现某种网络技术所要遵循的统一标准和规则。 二、OSI…...

服务网格实施周期缩短 50%,丽迅物流基于阿里云 ACK 和 ASM 的云原生应用管理实践
作者:王夕宁、 刘强、 华相 公司介绍 丽迅物流是百丽旗下专注于时尚产业、为企业提供专业物流及供应链解决方案的服务商。其产品服务主要包括城市落地配、仓配一体、干线运输及定制化解决方案。通过自研智能化物流管理平台,全面助力企业合作集约化发展…...

bpmnjs Properties-panel拓展(属性设置篇)
最近有思考工作流相关的事情,绘制bpmn图的工具认可度比较高的就是bpmn.js了,是一个基于node.js的流程图绘制框架。初始的框架只实现了基本的可视化,想在xml进行客制化操作的话需要拓展,简单记录下几个需求的实现过程。 修改基础 …...

Debian系统上通过NFS挂载远程服务器硬盘
步骤 1:配置远程服务器 在拥有硬盘内容的远程服务器上,进行以下配置: 安装NFS服务器软件: sudo apt-get update sudo apt-get install nfs-kernel-server编辑NFS服务器配置文件 /etc/exports,添加需要共享的目录及其权…...

【Linux】以太网协议以及MTU
以太网协议 数据链路层的功能以太网的数据格式MTUMTU对IP协议的影响MTU对UDP协议的影响MTU对TCP协议的影响 数据链路层的功能 数据链路层的主要功能是:控制链路。包括数据链路的建立、链路的维护和释放。MAC寻址也是它的功能,寻址是指计算机网卡的MAC地…...

UE5打完包后,启动程序不能全屏
最近看到ue5的打包程序后不能默认自动全屏,效果如下,发现并不是全屏的,而且就算点击放大也不是全屏 解决办法:设置如下之后在打包就可以了 但是会一直打印错误的日志,不过这个不影响使用...

财务部发布《企业数据资源相关会计处理暂行规定》
导读 财务部为规范企业数据资源相关会计处理,强化相关会计信息披露,根据《中华人民共和国会计法》和相关企业会计准则,制定了《企业数据资源相关会计处理暂行规定》。 加gzh“大数据食铁兽”,回复“20230828”获取材料完整版 来…...

引用(个人学习笔记黑马学习)
1、引用的基本语法 #include <iostream> using namespace std;int main() {int a 10;//创建引用int& b a;cout << "a " << a << endl;cout << "b " << b << endl;b 100;cout << "a "…...

卷积神经网络实现运动鞋识别 - P5
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍦 参考文章:Pytorch实战 | 第P5周:运动鞋识别🍖 原作者:K同学啊 | 接辅导、项目定制🚀 文章来源:K同学的学习圈子 目录…...

C#安装“Windows 窗体应用(.NET Framework)”
目录 背景: 第一步: 第二步: 第三步: 总结: 背景: 如下图所示:在Visual Studio Installer创建新项目的时候,想要添加windows窗体应用程序,发现里面并没有找到Windows窗体应用(.NET Framework)模板,快捷搜索也没有发现&#…...

SQL高阶语句
目录 1、概念 1.1、概述 1.2、常见的MySQL高阶语句的概念: 1.3、 SQL高阶语句的作用 2、常用查询 2.1、按关键字排序 2.1.1、概述和作用 2.1.2、 (1)语法 2.1.3、模板表:ky30 编辑2.1.4、分数按降序排列 2.1.5、ORDER…...

【交换机】如何通过Web方式登陆交换机
一、华为交换机web登陆配置 Web网管是一种对交换机的管理方式,它利用交换机内置的Web服务器,为用户提供图形化的操作界面。用户可以从终端通过HTTPS登录到Web网管,对交换机进行管理和维护,同时也非常方便。 一、配置思路ÿ…...

Flink CDC学习笔记
第一章 CDC简介 1.1 什么是CDC CDC (Change Data Capture 变更数据获取)的简称。核心思想就是,检测并获取数据库的变动(增删查改),将这些变更按发生的顺序记录下来,写入到消息中间件以供其它服务进行订…...

NEOVIM学习笔记
GitHub - blogercn/nvim-config: A pretty epic NeoVim setup 一直使用vim,每次到了新公司都要配置半天,而且常常配置失败,很多插件过期不好用。偶然看到别人的NEO VIM,就试着用了一下,感觉还不错。 用来开发和阅读C代…...

Docker三剑客之docker-compose
docker-compose 是 Docker 生态系统中的一个重要成员,它允许开发人员使用一个简单的配置文件来定义和运行多个 Docker 容器。通过 docker-compose,你可以定义应用程序的各个组件、容器之间的依赖关系以及网络配置,从而实现在一个命令中启动、…...

单调队列
目录 一,单调队列 二,模板实现 三,OJ实战 剑指 Offer 59 - I. 滑动窗口的最大值 一,单调队列 单调队列是双端队列的拓展,支持尾部插入,双端删除,其中的数据始终维持单调性,从而…...

effective c++ 笔记
TODO:还没看太懂的篇章 item25 item35 模板相关内容 文章目录 基础视C为一个语言联邦以const, enum, inline替换#define尽可能使用constconst成员函数 确定对象使用前已被初始化 构造、析构和赋值内含引用或常量成员的类的赋值操作需要自己重写不想使用自动生成的函…...