条款1:尽量用const和inline而不用#define
情况1:
#define ASPECT_RATIO 1.653,问题,ASPECT_RATIO不会加入到符号列表中。难以调试
解决方案:
const double ASPECT_RATIO = 1.653
指针情况:const char * const authorName = "Scott Meyers";
类常量:定义成静态成员:
class GamePlayer {
private:
static const int NUM_TURNS = 5; // constant declaration
int scores[NUM_TURNS]; // use of constant
...
};
上面的语句是NUM_TURNS的声明,而不是定义,所以你还必须在类的实现代码文件中定义类的静态成员:
const int GamePlayer::NUM_TURNS; // mandatory definition;
// goes in class impl. file
如果编译器不支持附初值,可以采用称之为“借用enum”的方法来解决
class GamePlayer {
private:
enum { NUM_TURNS = 5 }; // "the enum hack" — makes
// NUM_TURNS a symbolic name
// for 5
int scores[NUM_TURNS]; // fine
...
};
情况2:
#define max(a,b) ((a) > (b) ? (a) : (b)) 问题: int a = 5, b = 0;
max(++a, b); // a 的值增加了2次
max(++a, b+10); // a 的值只增加了1次
解决方案:
内联函数:
inline int max(int a, int b) { return a > b ? a : b;
模板函数:
template<class T>
inline const T& max(const T& a, const T& b)
{ return a > b ? a : b; }
在你打算用模板写象max这样有用的通用函数时,先检查一下标准库(见条款49),看看他们是不是已经存在。
条款2:尽量用<iostream>而不用<stdio.h>
scanf和printf很轻巧,很高效,但是,他们不是类型安全的,而且没有扩展性。
int i;
Rational r; // r 是个有理数
...
cin >> i >> r;
cout << i << r;
>>和<<必须是可以处理Rational类型对象的重载函数(可能要通过隐式类型转换)。另外,编译器自己可以根据不同的变量类型
选择操作符的不同形式,所以不必劳你去指定第一个要读写的对象是int 而第二个是Rational。
另外,在传递读和写的对象时采用的语法形式相同,所以不必象scanf 那样死记一些规定。
iostream 库的类和函数所提供的类型安全和可扩展性的价值远远超过你当初的想象。
class Rational {
public:
Rational(int numerator = 0, int denominator = 1);
...
private:
int n, d; // 分子,分母
friend ostream& operator<<(ostream& s, const Rational& r);
};
ostream& operator<<(ostream& s, const Rational& r)
{
s << r.n << '/' << r.d;
return s;
}
iostream 库的类和函数所提供的类型安全和可扩展性的价值远远超过你当初的想象。
可有些情况下回到那些经过证明而且正确的老路上去还是很有意义的。
- 有些iostream 的操作实现起来比相应的C stream效率要低
- 在标准化的过程中,iostream 库在底层做了很多修改,不同的厂商遵循标准的程度也不同。
- iostream 库的类有构造函数而<stdio.h>里的函数没有,在某些涉及到静态对象初始化顺序的时候,用标准C库会更简单实用。
我确实说的是<iostream>而非<iostream.h>。如果使用了#include <iostream>, 得到的是置于名字空间std(见条款28)下的iostream 库的元素;如果使用#include <iostream.h>,得到的是置于全局空间的同样的元素。
条款3:尽量用new和delete而不用malloc和free
malloc和free(及其变体)会产生问题的原因在于它们太简单:他们不知道构造函数和析构函数。
string *stringArray1 = static_cast<string*>(malloc(10 * sizeof(string)));
string *stringArray2 = new string[10];
stringArray1确实指向的是可以容纳10个string对象的足够空间,但内存里并没有创建这些对象,换句话说,stringArray1其实一点用也没有。相反,stringArray2 指向的是一个
包含10个完全构造好的string对象的数组,每个对象可以在任何读取string 的操作里安全使用。
在你后面的程序里你一定会这么做:
free(stringArray1);
delete [] stringArray2; // 参见条款5:这里为什么要加上个"[]"
调用free 将会释放stringArray1 指向的内存,但内存里的string对象不会调用析构函数。相反,当对stringArray2调用delete时,数组里的每个对象
都会在内存释放前调用析构函数。
把new和delete与malloc和free混在一起用也是个坏想法。其后果是不可预测的。大家都知道“不可预测”的意思:它可能在开发阶段工作良好,在测试阶段工作良好,但也可能会最后在你最重要的客户的脸上爆炸。
其实,在C++程序里使用malloc和free没有错,只要保证用malloc得到的指针用free,或者用new得到的指针最后用delete来操作就可以了。
你最好就什么时候都一心一意地使用new和delete吧
条款4:尽量使用C++风格的注释
C++风格://,C风格:/* */
旧的C注释语法在C++里还可以用,C++新发明的行尾注释语法也有其过人之处。例如下面这种情形:
if ( a > b ) {
// int temp = a; // swap a and b
// a = b;
// b = temp;
}
假设你出于某种原因要注释掉这个代码块。用C++形式的句法来注释掉这个程序块时,嵌在里面的最初的注释不受
影响,但如果选择C风格的注释就会发生严重的错误:
if ( a > b ) {
/* int temp = a; /* swap a and b */
a = b;
b = temp;
*/
}
C风格的注释当然还有它存在的价值。例如,它们在C和C++编译器都要处理的头文件中是无法替代的。
值得指出的是,有些老的专门为C写的预处理程序不知道处理C++风格的释。
#define LIGHT_SPEED 3e8 // m/sec (in a vacuum)
对于不熟悉C++的预处理程序来说,行尾的注释竟然成为了宏的一部分!