Item 19: 视 class design(类设计)为 type design(类型设计)
作者:Scott Meyers
译者:fatalerror99 (iTePub's Nirvana)
发布:http://blog.csdn.net/fatalerror99/
在 C++ 中,就像其它 object-oriented programming languages(面向对象编程语言),通过定义一个新 class(类)来定义一个新 type(类型)。作为一个 C++ 开发者,你的大量时间就这样花费在加大你的 type system(类型系统)上。这意味着你不仅仅是一个 class designer(类设计者),而且是一个 type designer(类型设计者)。重载函数和运算符,控制内存分配和回收,定义 object 的初始化和终结过程——这些全在你的掌控之中。因此你应该在 class design(类设计)中倾注大量心血,就像语言设计者在语言的 built-in types(内建类型)的设计中所倾注的心血。
设计良好的 classes 是具有挑战性的,因为设计良好的 types 是具有挑战性的。良好的 types 拥有简单自然的语法,符合直觉的语义,以及一个或更多高效的实现。在 C++ 中,一个缺乏计划的 class definition(类设计),不可能达到上述任何一个目标。甚至一个 class 的 member functions(成员函数)的执行特征可能受到它们被声明的方式的影响。
那么,如何才能设计高效的 classes 呢?首先,你必须理解你所面对的问题。实际上每一个 class 都需要你面对下面这些问题,其答案通常就导向你的设计的限制因素:
- 你的新 type(类型)的 objects 应该如何创建和销毁?这些如何做将影响到你的 class 的 constructors(构造函数)和 destructor(析构函数),以及内存分配和回收的函数(operator new,operator new[],operator delete,和 operator delete[] ——参见 Chapter 8)的设计,只要你写了它们。
- object initialization(对象初始化)和 object assignment(对象赋值)应该有什么不同?这个问题的答案决定了你的 constructors(构造函数)和你的 assignment operators(赋值运算符)的行为和它们之间的不同。这对于不混淆 initialization(初始化)和 assignment(赋值)是很重要的,因为它们相当于不同的函数调用(参见 Item 4)。
- passed by value(传值)对于你的新 type(类型)的 objects 意味着什么?记住,copy constructor(拷贝构造函数)定义了一个 type(类型)的 pass-by-value(传值)是如何实现的。
- 你的新 type(类型)的合法值的限定条件是什么?通常,对于一个 class 的 data members(数据成员)来说,仅有某些值的组合是合法的。那些组合决定了你的 class 必须维持的 invariants(不变量)。这些 invariants(不变量)决定了你必须在 member functions(成员函数)内部进行错误检查,特别是你的 constructors(构造函数),assignment operators(赋值运算符),和 "setter" 函数。它可能也会影响你的函数抛出的 exceptions(异常),和你的函数的exception specifications(异常规范)(你用到它的可能性很小)。
- 你的新 type(类型)是否适合放进一个 inheritance graph(继承图)中?如果你从已经存在的 classes 继承,你将被那些 classes 的设计所约束,特别是它们的函数是 virtual(虚拟)还是 non-virtual(非虚拟)(参见 Items 34 和 36)。如果你希望允许其它 classes 从你的 class 继承,将影响到你声明的函数是否为 virtual(虚拟),特别是你的 destructor(析构函数)(参见 Item 7)。
- 你的新 type(类型)允许哪种 type conversions(类型转换)?你的 type(类型)身处其它 types(类型)的海洋中,所以是否要在你的 type 和其它 types 之间有一些转换?如果你希望允许 type T1 的 objects implicitly(隐式)转换到 type T2 的 objects,你就要么在 class T1 中写一个 type conversion function(类型转换函数)(例如,operator T2),要么在 class T2 中写一个 non-explicit constructor(非显式构造函数),而且它们都要能够以单一 argument(实参)调用。如果你希望仅仅允许 explicit conversions(显示转换),你就要写执行这个转换的函数,而且你还需要避免使它们的 type conversion operators(类型转换运算符)或 non-explicit constructor(非显式构造函数)能够以一个 argument(实参)调用。(作为一个既是隐式又是显式转换函数的例子,参见 Item 15。)
- 对于新 type(类型)哪些运算符和函数有意义?这个问题的答案决定你应该为你的 class 声明哪些函数。其中一些是 member functions(成员函数),另一些不是(参见 Items 23,24 和 46)。
- 哪些标准函数不应该被接受?你需要将那些都声明为 private(私有)的(参见 Item 6)。
- 你的新 type(类型)中哪些成员可以被访问?这个问题的可以帮助你决定哪些成员是 public(公有)的,哪些是 protected(保护)的,以及哪些是 private(私有)的。它也可以帮助你决定哪些 classes 和/或 functions 应该是 friends(友元),以及一个 class 嵌套在另一个 class 内部是否有意义。
- 什么是你的新 type(类型)的 "undeclared interface"?出于对 performance(性能),exception safety(异常安全)(参见 Item 29),以及 resource usage(资源使用)(例如,锁和动态内存)的考虑,它提供哪种保证?你在这些领域提供的保证将强行约束你的 classes 的实现。
- 你的新 type(类型)有多大程度的通用性?也许你并非真的要定义一个新的 type(类型)。也许你要定义一个整个的类型家族。如果是这样,你不需要定义一个新的 class,而是需要定义一个新的 class template(类模板)。
- 一个新的 type(类型)真的是你所需要的吗?如果你只是要定义一个新的 derived class(派生类),以便让你可以为一个已存在的 class 增加一些功能,也许通过简单地定义一个或更多 non-member functions(非成员函数)或 templates(模板)能更好地达成你的目标。
回答这些问题是困难的,所以定义高效的 classes 是具有挑战性的。然而,如果做好了,在 C++ 中 user-defined classes(用户定义类)生成的 types(类型)至少可以和 built-in types(内建类型)一样好用,它会使一切努力都变的有价值。
Things to Remember
- class design(类设计)就是 type design(类型设计)。定义一个新 type(类型)之前,确保考虑了本 Item 讨论的所有问题。