一、系统跟踪类Trace的设计过程:
step1:简单版
class Trace { public: Trace() { noisy = 0; } void print(char* s) { if(noisy) print("%s", s); } void on() { noisy = 1; } void off() { noisy = 0; } private: int noisy; };
step2:增加重定向功能,可能输出到别的文件中。
class Trace { public: Trace() { noisy = 0; f = stdout; } Trace(FILE *ff) { noisy = 0; f = ff; } void print(char* s) { if(noisy) fprintf(f, "%s", s); } void on() { noisy = 1; } void off() { noisy = 0; } private: int noisy; FILE *f; };
步骤二的改动,基于这样一个事实:
printf(args);
等价于:
fprintf(stdout, args);
二、类设计者的核查表
1. 你的类需要一个构造函数吗?
足够复杂的类需要构造函数来隐藏它们的内部工作方式。
2. 你的数据成员是私有的吗?
通常,使用公有的数据成员不是什么好事,因为类的设计者无法控制对这些数据成员的访问。
3. 你的类需要一个无参构造函数吗?
如果一个类有了构造函数,而你想声明该类对象的时候不必显示地初始化它们,则必须显式地写一个无参构造函数。
例如:
class Point { public: Point(int p, int q): x(p), y(q) { } //... private: int x, y; }; Point p; //错误,没有合适的默认构造函数可用
Point q[100]; //错误,没有合适的默认构造函数可用
4. 是不是每个构造函数初始化所有的数据成员?
如果没有初始化所有的数据成员,很容易出现难以发现的错误。
5. 你的类需要析构函数吗?
注意:并不是所有存在构造函数的类都需要析构函数。
需要不需要析构函数,主要在于该类是否分配了资源,而这些资源又不会伴随成员函数而自动释放。特别是那些在构造函数中有 new 操作符的类,通常要在析构函数中用 delete 来释放相对应的资源。
- 6.你的类需要一个虚析构函数吗?
- 注意:当一个类用于不会被继承时,这个类是不需要虚析构函数的。
class Base { public: Base() { s = ""; } private: string s; }; class Dase : Base { public: Dase() { } private: string t; }; int main() { Base *b = new Dase(); delete b;//除非Base有一个虚析构函数,否则将调用错误的析构函数 }
7. 你的类需要一个复制构造函数吗?
8. 你的类需要一个赋值操作符吗?
9. 你的赋值操作符能正确地将对象赋值给对象本身吗?
10. 你的类需要定义关系操作符吗?
11. 删除数组时你记住用 delete [] 了吗?
12. 记得在复制构造函数和赋值操作符的参数类型中加上 const 了吗?
13. 如果函数有引用参数,它们应该是const引用吗?
14. 记得适当地声明函数为 const 的了吗?
15.