调试创建不同的代码。 (4)用 new 和 delete 代替 malloc 和 free; 在创建对象、类型的安全性和灵活性方面。使用 new/delete 比 malloc/free 要好。另外,new 可被重载,提供了更大的灵活性。 (5)用输入输出流(iostreams)代替 stdio。 使用 C++输入输出流(<<和>>)而不使用 C 标准输入输出库 (printf/sprintf 和 scanf/sscanf),有利于安全性和扩展性。从调试的角度 来看, 标准输入输出函数的最大问题在于编译器不能对控制流参数进行任何类型 检测,而输入输出流的任何
问题都能在编译时检测出来。 2 使用头文件 要在头文件中声明所有共享的外部符号, 而且保留函数原型中的参数名。 把所有的共享定义放在头文件中,不要在.cpp 文件里面看到 extern 关键字。 3 初始化变量 在使用变量之前一定要把它们初始化。在初始化之前就使用变量肯定会 产生错误。通常不需对对象进行初始化,对对数据成员应在构造函数中初始化。 必须明确地为在栈中和堆中分配的数组和数据结构进行初始化。对于对象,应该 初始化每个需要初始化的数据成员。因为变量的使用是由优化器来检查的,所以 检测未初始化的本地变量,发布版本要比调试版本要做得好。 4 使用布尔表达式 C++的布尔类型:bool,值为 true 和 false,大小为一个字节。 Windows 程序通常用 BOOL 类型。定义如下:
Typedef int BOOL; #define FALSE 0 #define TRUE 1 在 C++中,一个布尔表达式如果为 0 则为假,其
他则为真。因此,对布 尔表达式应该检查是否问假而不是检查是否为真。 5 使用句柄和指针 初始化一个指针时, 要么让其指向一个有效的内存地址, 要么设为 0 (空 指针),避免指针指向无效地址。回收指针所指对象时要重新初始化这个指针, 并且在指针被释放前为空时就对其进行处理。对句柄的处理跟指针一样。 6 用引用而不是指针做参数 用指针做函数的参数可传递一个空指针,很灵活,但也很容易忘了对指 针进行初始化。而引用是对象的别名,它必须和有效的对象相关联,不存在空的 和没有初始化的引用。当在函数中收到一个引用参数时,可以肯定这是一个有效 的对象。程序用引用做参数比用指针做参数更为健壮。 强制类型转换(cast) 7 强制类型转换(cast) 进行数据类型的强制类型转换时,将会调用相应的构造函数或转换函数 来创建一个新类型的临时对象。对指针的正确类型转换可消除一个编译错误,但 并没改变指针。强制类型转换破坏了编译器进行类型检查的功能,而这正是编译 器查找错误的最有效的机制。为了保证安全性,每一个强制类型转换都需要手工 进行类型检查。为尽量避免强制类型转换,你可以:避免使用多态数据类型;使 用更加广泛的基类;提供特殊的存取函数;让编译器隐式处理类型转换等措施。 8 使用构造函数和析构函数 构造函数需要分配内存,创建资源或者打开文件,这些运算并不总是成 功。构造函数没有返回值,没有直接显示错误的方法。一个常见的方法(在很多 MFC 类中使用)是把对象创建分为两步:第一步,让构造函数以一种不会出错的 方式初始化对象;第二步,让某些初始化函数(如 Init 或 Open)完成
工作,这 一步可能出错。另一种方法是在构造函数中使用异常:第一步,以不会出错的方 式初始化对象;第二步,用可能在 try 段内出错的代码初始化对象;第三步,在 catch 代码里面处理异常。如果出现异常,就会在构造函数里清除分配的资源, 并且再次抛出异常。 异常处理的一个关键细节就是在栈展开的过程中抛出的异常会终止整个 应用程序。在处理异常时经常要调用析构函数,因此析构函数很容易出错,一定 要保证析构函数的异常在析构函数中得到处理。要保证基类的析构函数是虚函 数。 这样, 就算对象是一个指向基类的指针, 也会调用派生类的析构函数。 否则