【More Effective C++】Item 4
Item 4:非必要不提供Default Constructor
所谓default constructor就是不需要提供任何自变量就可以被调用的构造函数,即它能在没有任何外来信息的情况下将对象初始化。
如果class constructor不能确保object的所有data member都能被正确的初始化,那么最好不要定义default constructor;虽然这样做会对class的使用方式带来一些限制,但同时也带来了一个保证:当你真的使用这样的class时,你可以预期该constructor产生的对象都是被完整初始化的。
那么什么时候应该提供default constructor,什么时候不应该提供呢?
在以下三种情况下,如果不提供default constructor会给class的使用方式带来限制:
第一种情况:产生数组时
总所周知,定义对象数组时,如果该class没有提供default constructor,一般而言没有任何方法可以为数组中的对象指定constructor自变量,所以几乎不能产生一个由class object 构成的数组。
但是,我们可以通过以下三种方法来解决以上问题。
1、通过构建no-heap数组,即 "className arrayName[10] = { classConstructor(Parameter1), classConstructor(Parameter2), ... , classConstructor(Parameter10) }; " 通过这样就可以为每一个数组对象元素赋以正确的自变量进行完全初始化。但是,这种方面不能延伸到heap数组。
2、使用指针数组代替对象数组;即 “typedef classType* PCT;” PCT arrayName[10];" 或者 “classType* pName = new PCT[10]”。这样数组中的各个指针元素就可以用来指向不同的class object,即 "for(int i=0; i<10; i++) arrayName[i] = new classConstructor(Parameteri);" 。但是,这个方法有两个缺陷:a)必须将该数组所指的对象删除,避免resource leak; b)浪费了存储空间,因为既要保存class object,又要存储指针。
3、使用“raw memory"分配足够的内存空间,再使用”placement new“在这块内存上构建class object;即 "void * pRawmemory = operator new[](10*sizeof(classType)); classType* pName = static_cast<classType*>(pRawmemory); for(int i=0; i<10; i++) new(&pName[i]) classConstructor(Parameteri);" 。但是,placement new方法大多数程序员不熟悉,很难进行维护;而且必须在数组对象的生命周期内,以手动方式调用对象的destructor ,最后还得经由operator delete[]的方式释放 raw memory。
第二种情况:不适用某些template-based container classes
因为缺乏default constructor,在使用这些template container时,如上不能初始化不提供自变量的数组等情况。
第三种情况:在virtual base class 中使用比较麻烦
因为virtual base class的constructor的自变量必须由欲产生object的派生层次最深的class提供。因此,每一层的derived class 都需要很清楚地知道和了解其意义,并且提供virtual base class的constructor所需的自变量,这样的确很麻烦。
因为上述因缺少default constructor带来的三种情况,有些人便认为所有的class都应该提供default constructor---甚至即使其default constructor没有足够信息将对象做完整的初始化。但是我能很明确的告诉你,这样做是很不好的。
原因一:因为member function不确定object的data member数据是否都是有效的,因此在使用前还需要进行验证,增大了复杂性;
原因二:影响了class的效率,包括测试行为的时间代价、测试代码的空间代价等等。
因此,正如标题所示,非必要不要提供default constructor。