草庐IT

《Effective C++》阅读总结(四): 设计、声明与实现

李慕清 2023-03-28 原文

第四章: 设计与声明

18. 让接口更容易被正确使用,不易被误用

  • 将你的class的public接口设计的符合class所扮演的角色,必要时不仅对传参类型限制,还对传参的值域进一步限制。

19. 设计class犹如设计type

  • 内置类型如int、float等,本质也是一个class,用户自定义的class的行为和状态应当与内置类型类似的。设计class时,首先要考虑构造和析构、然后是赋值操作如何实现、考虑class是否要继承某一已有class、尽量使你的class一般化等等需要考虑的问题。

20. 宁以pass-by-reference-to-const替代pass-by-value

  • 这个准则很常见,使用const引用传参以避免拷贝和修改入参。看具体场景,如果是需要修改入参,那么就不要加const、如果入参是简单内置类型,拷贝不怎么消耗资源,那么直接传值也是可以的。同样,使用指针也是一样的道理。此外,传class的引用或指针也可以避免对象切个问题,例如函数接受一个父类对象,当传入子类对象时,父类对象构造函数会被调用,此时子类的属性就会丢失,即切割问题,这是不希望的。

21. 必须返回对象时,不要妄想返回reference

  • 很显然的一点就是,你不能返回一个locakl对象的指针或者引用,因为,函数调用结束后,所有存储在栈上的local对象都将被销毁。此外,可以返回在函数内构造的堆对象的指针,但尽量不要返回其引用,引用如果在返回后没有保护好而被覆盖,则造成内存泄漏。

22. 将成员变量声明为private

  • 这是一个封装合理的class应当遵守的规则,成员变量声明为private的话,可以通过public接口间接控制成员变量,并加以特殊限制。这样做也有缺点,就是编写代码无法直接修改成员变量,代码量增加。如果只需要保存读取数据,不做其他操作,那么声明一个结构体是一个不错的选择。
    我们一把不使用protected成员,没什么用。

23. 宁以non-membernon-friend替换member函数

  • 如果一个操作是一般性的,并不是class特有的,那么将其抽离class单独声明定义。

24. 若所有参数都需要类型转换,请为此采用non-member函数

  • 这里的所有参数包括this指针,所以连this指针都需要转换类型,那么这个函数其实就不应该是这个class的成员函数,应当抽离class。

25. 考虑写一个不抛出异常的swap函数

  • 使用std::swap吧,我觉得目前够用了

第五章 实现

我们写c++代码,一个是如何设计架构,即定义class及其成员函数和成员数据,以及不同class之间的通讯关系;另一个是如何具体实现每个函数,对每个函数或成员函数的功能进行实现,这部分每个功能相对独立,比较底层,但其中也有些需要注意的点,主要有以下几个:

26. 尽可能延后变量定义式的出现时间

  • 这样可以提高程序的效率,但我并不认为会提高程序的可读性。对于那些不是非常在意运行效率的函数,可读可维护性要排在效率前面,如初始化,而那些会循环调用很多次的代码,如模型推理计算,效率至上。此外,编译器也会尽可能优化代码,以提高运行时效率。

27. 尽量少做转型动作

C++中的cast方法有四种:
const_cast:用于移除对象身上的const属性,只此一个功能。常用。低风险。
static_cast:用于强制隐式转换。例如将int转为double,将基类指针转为子类指针时不进行安全检查。不可用于移除const属性。常用。低风险。
dynamic_cast:用于执行类继承体系中安全向下转型。也就是用来决定某个对象是否归属类继承体系中的某个类型。比如可以将多态基类(包含虚函数的基类)的指针强制转换为派生类的指针,很耗时,不常用。高风险。
reinterpret_cast:用于执行低级转型,例如将int*转为int,执行的是逐个比特复制的操作。 不用。高风险。

  • 所以,首先尽量避免转型,或者对于不可避免的转型,将转型操作隐藏到函数里面,并且尽量使用C++style的转型方法,不要使用早期C风格的那种方法。在类继承体系中进行上行转换时,dynamic_caststatic_cast效果一样且安全,但下行转换时,dynamic_cast会进行类型安全检查且耗时,而static_cast不进行检查。所以如果有必要且明确基类指针是哪个子类时,通常使用static_cast即可。
    移除const属性是危险的,如无十分必要请不要这样做,肯定有其他方法规避这种危险操作。

28. 避免返回handle指向对象的内部成分

  • 即不要返回指向对象数据成员的指针或引用,以保证对象的封装性,防止外部改变对象内部数据。如果非要这样做,请将返回值加上const属性,以禁止修改。

29. 为异常安全所做的努力是值得的

  • 这条准则不好总结,大概来说就是对那些可能导致异常发生的函数进行异常捕获并进行适当的处理,以避免内存泄漏。

30. 透彻了解inline的里里外外

  • 直接定义在class体内的成员函数默认是内联的,也可以在class体外显式声明内联函数,但最终是否执行内联替换由编译器决定,它只会将那些小型、频繁调用的函数编译为内联函数,这样能尽可能降低代码膨胀并提升程序执行速度。

31. 将文件间的编译依存关系降至最低

  • 即让代码依赖声明式而非定义式,这样定义式发生改变不会导致依赖于该定义式的其他代码也需要重新编译。基于此构想的两种方案是:Handle classinterface class。这是在我们平时构建工程的时候经常遇到的两种设计方法。
  • 程序库头文件应该以“完全且仅有声明式”的形式存在。其他代码依赖此文件即可。

小结:

以上即总结。明天上班。

有关《Effective C++》阅读总结(四): 设计、声明与实现的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby-on-rails - active_admin 目录中的常量警告重新声明 - 2

    我正在使用active_admin,我在Rails3应用程序的应用程序中有一个目录管理,其中包含模型和页面的声明。时不时地我也有一个类,当那个类有一个常量时,就像这样:classFooBAR="bar"end然后,我在每个必须在我的Rails应用程序中重新加载一些代码的请求中收到此警告:/Users/pupeno/helloworld/app/admin/billing.rb:12:warning:alreadyinitializedconstantBAR知道发生了什么以及如何避免这些警告吗? 最佳答案 在纯Ruby中:classA

  3. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  4. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  5. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  6. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  7. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  8. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  9. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  10. SPI接收数据异常问题总结 - 2

    SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

随机推荐