Effective c++ 读书笔记

说明

学习语言本身是一回事,学习如何使用这种语言进行设计和高效编程是另一回事。

这本书就是在有了一定的基础知识后,在实际项目开发和后续学习中的必读经典。它基于编程实际,以条款的形式描述了需要避开的坑和需要遵守的高效原则。

读过《c缺陷与陷阱》的朋友,可能在读这本书时能找到当时相同的感觉。

一口气读完了本书,余音绕梁,意犹未尽。重新翻阅本书,并作此记录,以供后续复习之用。

主要内容

习惯c++
  1. c++是一个语言联邦,它由四个部分组成:c, Object-Oriented c++, Template c++, STL。
  2. 在使用#define的绝大部分场合下,使用 const/enum (对于单纯常量) 或者 inline (对于形似函数的宏)更好。
  3. 尽量使用const,包括函数参数、返回类型、成员函数、对象等,告知编译器提前报出错误并减少不必要的麻烦。
  4. 尽量在构造函数中使用成员初始化列表语法进行初始化,绝对不要在初始化前使用对象,特别是内置类型,这会导致不确定的行为。
构造、析构、赋值
  1. 编译器会在需要的时候自动生成default构造函数、copy构造函数、copy赋值操作符、析构函数,它们是public且inline的。
  2. 如果不想让编译器默认生成上述函数,应该明确拒绝,如把成员函数声明为private且不提供实现、继承uncopyable类等,c11中使用=delete。
  3. 一定要为多态基类声明virtual析构函数,否则可能造成派生类内存泄漏,导致未定义的行为。如果不是作为基类使用,或者不具备多态性,就不要使用virtual析构函数。
  4. 如果析构函数中可能抛出异常,就应该在析构函数内把它处理掉
  5. 绝对不能在构造和析构函数中调用virtual函数,这个问题可以参考我之前的一篇博文:c++ 纯虚函数被调用 错误原因
  6. 令赋值=操作符返回一个reference to *this。
  7. 在赋值=操作符中处理“自我赋值”,注意比较来源对象和目标对象的地址、语句顺序的安排和copy-and-swap技术。
  8. copy复制函数应确保复制对象内的所有成员变量,包括基类部分,否则编译器不会提醒你,可能导致更严重的后果 ———— 加班。
资源管理
  1. 使用对象来管理资源(如内存、fd、互斥锁、数据库连接、网络sockets等),利用对象的自动析构来自动释放资源。
  2. 注意资源对象的copy操作,如auto_ptr, unique_ptr, shared_ptr处理copying的行为就不同。
  3. 在资源管理类中提供对原始资源的访问,不管是扩展性还是第三方库,都可能有这个需求,如shared_ptr可以通过get获得裸指针。
  4. new和delete要配对使用。
  5. 以独立语句将new获得的对象置入智能指针,因为语句执行顺序的不确定性可能导致中间步骤抛出异常而导致内存泄漏。
设计与声明
  1. 让接口容易被使用,不易被误用。这一条可能会伴随大家的职业生涯了。接口一致性、与内置类型的行为兼容等。
  2. 像设计type一样去设计你自己的类。想想String类具有的接口和功能吧!
  3. 尽量以pass-by-reference-to-const替换pass-by-value,避免无谓的构造和析构。但对于内置类型及STL来说,传值更好。
  4. 必须返回对象时,绝对不要返回局部对象的指针或者引用,返回副本就ok了。
  5. 切记将成员变量声明为private,protected的封装性并不比public好多少。
  6. 有时候,使用non-member non-friend函数反而能够增加封装性和扩展性。
  7. 若所有参数都需要类型转换,就使用non-member函数吧!
  8. 写出一个不抛出异常的swap函数是值得的。
实现
  1. 尽可能延后变量定义式出现的时间,需要使用的时候再定义。
  2. 尽量少做转型,使用其他设计替代。如果非做不可,使用c++的显式转型,可以参考我之前的博文 c++类型转换详解,注意 dynamic_cast 效率低下。
  3. 不要向外部返回指向内部成分的handle,否则封装性不保,且可能导致诸如 dangling handles等不可预知的行为。
  4. 为异常安全做出努力是值得的,三个境界:基本承诺、强烈保证、不抛出异常保证。
  5. 声明函数为inline之前,慎重考虑一下是否值得。
  6. 尽量减少文件的编译依赖。
继承与面向对象设计
  1. 先确定is-a关系,再决定使用public继承。记住:对于public继承,所有适用于基类的事情也一定适用于派生类。
  2. 派生类内不要使用基类同名函数,可以使用using声明式。
  3. 区分接口继承和实现继承,如纯虚函数和虚函数(具有默认实现)及基类的成员函数(不能重写,只能继承实现)的区别。
  4. 除了virtual函数,你可以有其他选择,如模板模式、策略模式、函数指针等,知道总比不知道强。
  5. 不要重新定义继承而来的non-virtual函数。
  6. 不要重新定义继承而来的缺少参数值,此处无法动静结合。
  7. has-a与根据某物实现出————复合。
  8. 除非非使用private继承不可,就不要使用,总有替代设计可能更好。
  9. 明智而审慎地使用多重继承。意思就是没事不要使用。
模块与泛型编程
  1. 隐式接口和编译期多态,考虑一下template具体化和函数重载解析,这些都是编译期完成的。
  2. typename具有双重意义:template的参数关键字、标识嵌套从属类型。
  3. 处理模板化基类内的名称,通过this->指向基类模板成员名称或者写出基类修饰符。
  4. 给template瘦身,将无关参数抽离。
  5. 使用成员函数模板接受兼容类型。
  6. 使用非成员函数进行类型转换。
  7. 使用traits classes表现类型信息。
  8. template元编程,将工作由运行期迁移到编译期,提高效率。
定制new和delete
  1. new-handler的行为。
  2. 在需要改善效能、对heap错误调试时可能需要自行写出new和delete。
  3. 编写new 和 delete时需要固守常规。
  4. placement new 和placement delete成对出现。
杂项
  1. 编译器0警告是每个开发人员的小目标。当然别在编译选项里把警告屏蔽了哈!
  2. 熟悉标准程序库。知道的多了,解决问题的方法自然就多了。
  3. 闲下来的时候多看看boost库,因为它能让你少加班啊!
总结

字字珠玑。

在平凡的代码中显示着屏幕前各位的思想,而这与探索深度的原理和知识密不可分。

多读书,多思考,才能写出来对得起电脑配置的代码啊!

[手动捂脸,自行脑补]

参考资料

《Effective c++》

你可能感兴趣的:(读书笔记,cpp,c++,effective,读书笔记)