《Effective C++》阅读总结(四): 设计、声明与实现
第四章: 设计与声明
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-member
、non-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_cast
和static_cast
效果一样且安全,但下行转换时,dynamic_cast
会进行类型安全检查且耗时,而static_cast
不进行检查。所以如果有必要且明确基类指针是哪个子类时,通常使用static_cast
即可。
移除const属性是危险的,如无十分必要请不要这样做,肯定有其他方法规避这种危险操作。
28. 避免返回handle指向对象的内部成分
- 即不要返回指向对象数据成员的指针或引用,以保证对象的封装性,防止外部改变对象内部数据。如果非要这样做,请将返回值加上const属性,以禁止修改。
29. 为异常安全所做的努力是值得的
- 这条准则不好总结,大概来说就是对那些可能导致异常发生的函数进行异常捕获并进行适当的处理,以避免内存泄漏。
30. 透彻了解inline的里里外外
- 直接定义在class体内的成员函数默认是内联的,也可以在class体外显式声明内联函数,但最终是否执行内联替换由编译器决定,它只会将那些小型、频繁调用的函数编译为内联函数,这样能尽可能降低代码膨胀并提升程序执行速度。
31. 将文件间的编译依存关系降至最低
- 即让代码依赖声明式而非定义式,这样定义式发生改变不会导致依赖于该定义式的其他代码也需要重新编译。基于此构想的两种方案是:
Handle class
和interface class
。这是在我们平时构建工程的时候经常遇到的两种设计方法。 - 程序库头文件应该以“完全且仅有声明式”的形式存在。其他代码依赖此文件即可。
小结:
以上即总结。明天上班。