在 C++ 中,就像其它面向对象编程语言,可以通过定义一个新的类来定义一个新的类型。作为一个C++开发者,你的大量时间就这样花费在扩张你的类型系统。这意味着你不仅仅是一个类的设计者,而且是一个类型的设计者。重载函数和运算符,控制内存分配和回收,定义对象的初始化和终结过程——这些全在你的掌控之中。因此你应该在类设计中倾注大量心血,就如语言设计者在语言内置类型设计中所倾注的大量心血。
设计良好的类是有挑战性的,因为设计良好的类型是有挑战性的。良好的类型拥有简单自然的语法,符合直觉的语义,以及一个或更多高效的实现。那么,如何才能设计高效的类呢?首先,你必须理解你所面对的问题。实际上每一个类都需要你面对下面这些问题,其答案通常就导向你的设计规范:
· 新类型的对象应该如何创建和销毁?如何做这些将影响到你的类的构造函数和析构函数,以及内存分配和回收函数(operator new,operator new[],operator delete,和 operator delete[])的设计,除非你不写它们。
· 对象的初始化和对象的赋值应该有什么不同?这个问题的答案决定了你的构造函数和赋值运算符的行为以及它们之间的不同。
· 值传递(passed by value)对于新类型的对象意味着什么?拷贝构造函数定义了一个新类型的传值如何实现。
· 新类型的合法值是什么?通常,对于一个类的数据成员来说,仅有某些值的组合是合法的。那些数值集决定了你的类必须维护的约束条件。也决定了必须在成员函数内部进行的错误检查,特别是构造函数,赋值运算符,以及"setter"函数。它可能也会影响函数抛出的异常,以及(极少被使用的)函数异常明细(exceptionspecification)。
· 你的新类型需要配合某个继承图系中?如果你从已经存在的类继承,你就受到那些类的设计约束,特别受到它们的函数是virtual还是non-virtual的影响。如果你希望允许其他类继承你的类,将影响到你是否将函数声明为virtual,特别是你的析构函数。
· 你的新类型允许哪种类型转换?你的类型身处其它类型的海洋中,所以是否要在你的类型和其它类型之间有一些转换?如果你希望允许 T1 类型的对象隐式转型为 T2 类型的对象,你就要么在T1类中写一个类型转换函数(如operator T2),要么在 T2 类中写一个non-explicit-one argument构造函数。如果你只允许显示构造函数存在,就得写出专门负责执行转换的函数,且不得为类型转换操作符或non-explicit-oneargument构造函数。
· 对于新类型哪些运算符和函数是合理的?这个问题的答案决定你为你的类声明哪些函数。其中一些是成员函数,另一些不是。
· 哪些标准函数应该驳回?你需要将那些都声明为 private。
· 你的新类型中哪些成员可以被访问?这个问题的可以帮助你决定哪些成员是 public,哪些是 protected,以及哪些是 private。它也可以帮助你决定哪些类 和/或 函数应该是友元,以及一个类嵌套在另一个类内部是否有意义。
· 什么是新类型的未声明接口 "undeclaredinterface"?它对于效率,异常安全,以及资源使用(例如,多任务锁定和动态内存)提供哪种保证?你在这些领域提供的保证将为你的类的实现代码加上相应的约束条件。
· 你的新类型有多大程度的通用性?也许你并非真的要定义一个新的类型,也许你要定义一整个类型家族。如果是这样,你就不该定义一个新的类,而应该定义一个新的类模板。
· 一个新的类型真的是你所需要的吗?是否你可以仅仅定义一个新的继承类,以便让你可以为一个已有的类增加一些功能,也许通过简单地定义一个或更多非成员函数或模板能更好地达成你的目标。
· 类设计就是类型设计。定义高效的类是有挑战性的。在C++中用户自定义类生成的类型最好可以和内建类型一样好。