20.设计class犹如设计type

设计良好的class如同内置类型(type)一样,接口自然,目的明确。


在设计class之前,设计者必须扪心自问:

  • 新type的对象应该如何被创建和销毁?
    该问题的回答决定了构造函数、析构函数、内存分配和释放函数

  • 对象的初始化和赋值有何区别?
    该问题的回答决定了构造函数与赋值操作符的的行为。尤其需要注意的是,“初始化”、“赋值”是完全不同的概念,它们调用了不同的函数。

  • 新type的对象如果被pass by value,意味着什么?
    具体实现方式可参考Effective C++ 14;

  • 新type的合法值是什么?
    对于成员变量而言,可能仅有某些数值集有效。这些数值集决定了class的约束条件,同时也决定了setting函数(构造、赋值等)必须进行的错误检查,同时,它也影响函数抛出的异常。

  • 新type需要配合某个继承体系吗?
    如果当前type继承自某些class,那你必然受到了那些class设计的束缚,尤其是virtual或者non-virtual成员函数的影响。(详见Effective C++ 34/36/37/38)
    如果你允许别的类继承该type,那请谨慎考虑析构函数是否需要virtual。(Effective C++ 8)

  • 新type需要什么样的转换?
    如果你允许T1被隐式转换为T2,就必须在T1内写一个类型转换函数,或者为T2写一个non-explicit-one-argument(可被单一实参调用)的构造函数。(More Effective C++ 5)
    如果你只允许explicit构造函数存在,就必须撰写显式类型转换函数,并且禁止定义类型转换操作符或者non-explicit-one-argument构造函数。

  • 新type需要哪些操作符和成员函数?
    该问题的回答决定了class中需要声明的函数,其中有一些是成员函数,有些则不应该是。(Effective C++ 24,25,47)

  • 新type应当明确禁止哪些函数?
    该问题的回答决定了class中哪些函数需要声明为没有定义的private成员函数。(Effective C++ 6)

  • 谁会试图使用新type的成员?
    该问题的回答决定了成员函数的可见性,并且也决定了friend。

  • type的一般化程度如何?
    如果一般化的程度足够高,或许我们不应该定义一个class,而是应该定义一个class template。

  • 是否真的需要一个新type?
    如果你只是想增加了一个derived class为原有的class添加功能,先试试看能不能用一些non-member函数或者templates解决。


每一次写下class之前请默读本章所有问题。