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

《Effective C++》学习笔记 续

条款31:将文件间编译依存关系降至最低

请记住:

  • 支持”编译依存性最小化“的一般构想是:相依于声明式,不要相依于定义式。基于此构想的两个手段是Handle class和Interface class
  • 程序库头文件应该以”完全且仅有声明式“的形式存在。这种做法不论是否涉及template都适用

pimpl手法:在一个类中存放具体实现类的指针。这样,指针大小在32位系统上固定是4字节,无需知道具体实现类的大小。这解决了”接口与实现的分离“。是一种Handle class。

降低编译依存关系:

  • 如果使用object reference或object pointers可以完成任务,就不要使用objects
  • 如果能够,尽量以class声明替换class定义
  • 为声明式和定义式提供不同的头文件

抽象基类是interface class。

Handle class和Interface class可降低编译依存性。
 

条款32:确定你的 public 继承塑模出 is-a 关系

请记住:

  • public继承意味着is-a。适用于base class身上的每一件事一定也适用于derived class身上,因为每一个derived class对象也都是一个base class对象。

条款33:避免遮掩继承而来的名称

内层作用域的名称会遮掩外围作用域的名称

请记住:

  • derived class内的名称会遮掩base class内的名称(一般是一个类中同名函数多个重载的情况)。在public继承下从来没有人希望如此。
  • 为了让被遮掩的名称重见天日,可使用using声明式或转交函数(有一点理解,using)。

如果只想继承父类的部分函数,就可以使用private继承,然后设置一个转交函数,转交函数中使用父类作用域调用父类函数。

条款中,父类有多个重载函数的的情况,不论是虚函数还是非虚函数,都会发生遮掩。
 

 using 可以帮助我们获得base 中被遮掩的函数,但是如果我们只想要多个重载函数中的一个而不是全部的时候该怎么办呢?可以写一个简单的转交函数:

条款34:区分接口继承和实现继承

请记住:

  • 接口继承和实现继承不同。在 public 继承之下,derived classes 总是继承 base class 的接口
  • pure virtual 函数只具体指定接口继承
  • 简朴的(非纯)impure virtual 函数具体指定接口继承及缺省实现继承
  • non-virtual 函数具体指定接口继承以及强制性实现继承

条款35:考虑 virtual 函数以外的其他选择

  1. NVI手法:non-virtual interface

条款36:绝不重新定义继承而来的 non-virtual 函数

请记住:

  • 绝不重新定义继承而来的non-virtual函数
  1. 为什么?因为,重新定义non-virtual函数会打破is-a的关系。

条款37:绝不重新定义继承而来的缺省参数值

 对象的所谓静态类型(static type)就是它在程序中被声明时所采用的类型

virtual 函数是动态绑定的,但是缺省参数却是静态绑定的。意思是你可能会在“调用一个定义于 derived class”内的 virtual 函数的同时,却使用 base class 为它所指定的缺省参数值

请记住:

  • 绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,而 virtual 函数--你唯一应该覆写的东西--却是动态绑定

条款38:通过复合塑模出 has-a 或“根据某物实现出”

请记住:

  • 复合的意义和public继承完全不同
  • 在应用领域,复合意味着has-a。在实现领域,复合意味着is-implemented-terms-of(根据某物实现出)
  1. 复合有一些同义词:分层、内含、聚合、内嵌

条款39:明智而审慎地使用 private 继承

请记住:

  • private继承意味着implemented-in-terms-of(根据某类实现出)。他通常比复合的级别低。但是当derived class需要访问protected base class的成员,或需要重新定义继承而来的virtual函数时,这么设计是合理的。
  • 和复合不同,private继承可以造成empty base最优化(EBO,空白基类最优化)。这对致力于”对象尺寸最小化“的程序库开发人员而言,可能很重要。
     

如果 classes 之间地继承关系是 private,编译器不会自动将一个 derived class 对象转换成一个 base class 对象。由 private base class 继承而来的所有成员,在 derived class 中都会变成 private 属性

当 class 不带任何数据,没有 non-static 成员变量,没有 virtual 函数,也没有 virtual base classes 于是这种所谓的 empty classes 对象不适用任何空间,因为没有任何隶属对象的数据需要存储。然后由于技术上的理由,C++裁定凡是独立(非附属)对象都必须有非零大小。假设Empty就是这样的一个类,在大多数编译器中 sizeof(Empty) 获得 1 ,通常C++官方勒令默默安插一个 char 到空对象中。如果有对齐要求,可能不止1个char 大小

条款40:明智而审慎地使用多重继承

请记住:

  • 多重继承比单一继承复杂。他可能导致新的歧义性,以及对virtual继承的需要
  • virtual继承会增加大小、速度、初始化以及赋值复杂度等等成本。如果virtual base class不带任何数据,将是最具实用价值的情况
  • 多重继承的确有正当用途。其中一个情节涉及public继承某个Interface class和private继承某个协助实现的class的两相结合。
  1. 之所以用virtual继承,是因为在菱形继承体系中,基类的一个变量可能会导致孙类里面有两个副本,这就乱套了。
  2. 虚继承的原理:实际上,vbptr指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚表(virtual table),虚表中记录了vbptr与本类的偏移地址;第二项是vbptr到共有基类元素之间的偏移量。
     

条款41:了解隐式接口和编译期多态

对函数重载解析的一点理解:具现化一个模板函数,那么就是给T指定了类型,根据C++的编译法则,这个函数后面会跟上这个类型来描述这个函数名,这会引发多态。

以不同的 template 参数具现化 “function templates” 会导致调用不同的函数,这便是所谓的编译期多态(compile-time polymorphism)

运行期多态和编译期多态之间的差异:类似于 哪一个重载函数该被调用(发生在编译期)和 哪一个 virtual 函数该被绑定(发生在运行期)之间的差异

请记住:

  • class和template都支持接口和多态
  • 对class而言,接口是显示的,以函数签名为中心。多态则是通过virtual函数发生于运行期
  • 对template参数而言,接口是隐式的,奠基于有效表达式。多态则是通过template具现化和函数重载解析发生于编译期。

条款42:了解 typename 的双重意义

请记住:

  • 声明template参数时,前缀关键字class和typename可互换
  • 请使用关键字typename标识嵌套从属类型名称;但不得在base class list或member initialization list内以他作为base class的修饰符

条款43:学习处理模板化基类内的名称

请记住;

  • 可在 derived class templates 内通过 “this->” 指涉 base class templates 内的成员名称,或籍由一个明白写出的 base class 资格修饰符完成

所谓基类资格修饰符:using、作用域说明符::

这种情况:继承模板类调用模板父类的函数,如果像普通继承体系下那么调用,编译器是找不到父类的这个被调用函数的。这种情况出现的原因是:当编译器看到继承模板类时,模板父类并没有被具现化,那么编译器就不知道父类看起来像什么,也就没办法找到父类的成员函数。

模板全特化:类型参数被全部定义,没有其他模板参数可定义
 

条款44:将与参数无关的代码抽离 template

class templates 的成员函数只有被使用时才暗中具现化

请记住:

  • Template生成多个class和多个函数,所以任何template代码都不应该与某个造成膨胀的template参数产生相依关系(比如说非类型参数)
  • 因非类型模板参数而造成的代码膨胀,往往可消除,做法是以函数参数或class成员变量替换template参数
  • 因参数类型造成的代码膨胀,往往可降低,做法是带有完全相同的二进制表述的具现类型共享实现码(比如说类型int和类型long作为模板类的类型时,应该共享代码)
     

条款45:运用成员函数模板接受所有兼容类型

member function template(常简称为 member templates),其作用是为 class 生成函数:

请记住:

  • 请使用 member function templates( 成员函数模板)生成”可接受所有兼容类型“的函数
  • 如果你生命成员函数模板用于”泛化copy构造“或”泛化assignment“操作,你还是要声明正常的copy构造函数和copy assignment操作符。

条款46:需要类型转换时请为模板定义非成员函数

  1. 为什么?因为在template实参推导过程中,从不将隐式类型转换函数纳入考虑。意思是,你想要在一个地方隐式类型转换,但是template没意识到这个事。

请记住:

  • 当我们编写一个 class template,而它所提供之“与此 template 相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template 内部的 friend 函数”。

条款47:请使用 traits classes 表现类型信息

请记住:

  • traits calss使得类型相关信息在编译期可用。他们以template和template特化完成实现
  • 整合重载技术后,tarits class有可能在编译期对类型执行if…else测试

 

 

上述的重载函数是根据 tarits 的类型直接在编译期间就可以确定调用哪一个函数,而放在 if else 里面判断的时候需要等到运行时才能确定

  1. 五种类型的迭代器:

    • Input迭代器:只能一步一步向前移动,只读
    • Output迭代器:只能一步一步向前移动,只写
    • Forward迭代器:只能一步一步向前移动,可读可写
    • Bidirectional迭代器:双向移动
    • Random Access迭代器:双向随机跳跃

条款48:认识 template 元编程

Template metaprogramming (TMP 模板元编程)是编写 template-based C++ 程序并执行于编译期的过程。

参考文章:《Effective C++》笔记_effectivec++ 笔记-CSDN博客

相关文章:

  • 【控制器局域网】CAN报文学习笔记(四)之 字节排序、信号提取实例1
  • MyBatis——MyBatis的原始Dao开发(了解)
  • 类和对象(中篇)
  • SpringBlade export-user SQL 注入漏洞复现
  • Qt 国际化——创建中英文翻译步骤
  • Linux线程——条件变量
  • 快速能访问服务器的文件
  • Web请求与响应
  • 修改blackd源码,实现 black + isort
  • 银行测试:第三方支付平台业务流,功能/性能/安全测试方法
  • 最新国内可用使用GPT4.0,GPT语音对话,Midjourney绘画,DALL-E3文生图
  • MySQL基本操作 DDL DML DQL三大操作介绍
  • vue打包内存问题解决办法<--- Last few GCs ---><--- JS stacktrace --->
  • linux驱动的学习 驱动开发初识
  • 命令执行 [SWPUCTF 2021 新生赛]babyrce
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 【译】理解JavaScript:new 关键字
  • docker-consul
  • JSDuck 与 AngularJS 融合技巧
  • mongodb--安装和初步使用教程
  • python docx文档转html页面
  • Python 反序列化安全问题(二)
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • vue自定义指令实现v-tap插件
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 前端攻城师
  • 数组大概知多少
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 移动端 h5开发相关内容总结(三)
  • 分布式关系型数据库服务 DRDS 支持显示的 Prepare 及逻辑库锁功能等多项能力 ...
  • #宝哥教你#查看jquery绑定的事件函数
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (四)linux文件内容查看
  • (算法二)滑动窗口
  • (原創) 物件導向與老子思想 (OO)
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例
  • *(长期更新)软考网络工程师学习笔记——Section 22 无线局域网
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
  • .net wcf memory gates checking failed
  • .net2005怎么读string形的xml,不是xml文件。
  • .net连接MySQL的方法
  • .NET连接数据库方式
  • .NET中的十进制浮点类型,徐汇区网站设计
  • .pings勒索病毒的威胁:如何应对.pings勒索病毒的突袭?
  • @Responsebody与@RequestBody
  • [1181]linux两台服务器之间传输文件和文件夹
  • [AR]Vumark(下一代条形码)
  • [C#] 如何调用Python脚本程序
  • [C#][opencvsharp]opencvsharp sift和surf特征点匹配
  • [C\C++]读入优化【技巧】
  • [C++] Boost智能指针——boost::scoped_ptr(使用及原理分析)
  • [c++] 什么是平凡类型,标准布局类型,POD类型,聚合体