首页 > 代码库 > [2014-11-24]高质量C++C编程指南 - 阅读笔记

[2014-11-24]高质量C++C编程指南 - 阅读笔记

  1. C++ 语言可以用const来定义常量,也可以用 #define来定义常量。但是前者比后者有更多的优点:
    • const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。
    • 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
  2. 需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义文件的头部。为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中。
  3. 不能在类声明中初始化const数据成员。以下用法是错误的,因为类的对象未被创建时,编译器不知道SIZE的值是什么。

    class A

    {…

    const int SIZE = 100; // 错误,企图在类声明中初始化const数据成员

    int array[SIZE]; // 错误,未知的SIZE

    };

  4. const数据成员的初始化只能在类构造函数的初始化表中进行
  5. 断言assert是仅在Debug版本起作用的宏,它用于检查“不应该”发生的情况。
  6. assert不是函数,而是宏。程序员可以把assert看成一个在任何系统状态下都可以安全使用的无害测试手段。如果程序在assert处终止了,并不是说含有该assert的函数有错误,而是调用者出了差错,assert可以帮助我们找到发生错误的原因。
  7. 引用的一些规则如下:

    (1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。

    (2)不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。

    (3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。

  8. 内存分配方式有三种:

      (1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。

      (2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

      (3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

  9. 如果函数的参数是一个指针,不要指望用该指针去申请动态内存。
  10. 如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”,
  11. 由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态内存。
  12. 不要用return语句返回指向“栈内存”的指针
  13. (1)指针消亡了,并不表示它所指的内存会被自动释放。

    (2)内存被释放了,并不表示指针会消亡或者成了NULL指针。

  14. “野指针”不是NULL指针,是指向“垃圾”内存的指针。
  15. “野指针”的成因主要有两种:

    (1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。

    (2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。

  16. 通常有三种方式处理“内存耗尽”问题:
    • 判断指针是否为NULL,如果是则马上用return语句终止本函数。
    • 判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行。
    • 为new和malloc设置异常处理函数。
  17. 对于32位以上的应用程序,“内存耗尽”错误处理程序毫无用处。因为32位操作系统支持“虚存”,内存用完了,自动用硬盘空间顶替。
  18. 所有的一元运算符

    建议重载为成员函数

    = () [] ->

    只能重载为成员函数

    += -= /= *= &= |= ~= %= >>= <<=

    建议重载为成员函数

    所有其它运算符

    建议重载为全局函数

  19. 预处理器用复制宏代码的方式代替函数调用,省去了参数压栈、生成汇编语言的CALL调用、返回参数、执行return等过程,从而提高了速度。使用宏代码最大的缺点是容易出错,预处理器在复制宏代码时常常产生意想不到的边际效应。
  20. 在C++ 程序中,应该用内联函数取代所有宏代码,“断言assert”恐怕是唯一的例外。
  21. 定义在类声明之中的成员函数将自动地成为内联函数
  22. C++编译器将自动为A产生四个缺省的函数,如
  23. A(void); // 缺省的无参数构造函数

    A(const A &a); // 缺省的拷贝构造函数

    ~A(void); // 缺省的析构函数

    A & operate =(const A &a); // 缺省的赋值函数

    (1)如果使用“缺省的无参数构造函数”和“缺省的析构函数”,等于放弃了自主“初始化”和“清除”的机会,C++发明人Stroustrup的好心好意白费了。

    (2)“缺省的拷贝构造函数”和“缺省的赋值函数”均采用“位拷贝”而非“值拷贝”的方式来实现,倘若类中含有指针变量,这两个函数注定将出错。

[2014-11-24]高质量C++C编程指南 - 阅读笔记