避免无用的默认构造函数

C++允许在构建对象的时候使用默认构造函数。这一点为编程带来了一定程度上的便利,但同时也带来了一些实现陷阱。

有的时候使用默认构造函数所创建的对象反而没有实际意义,例如一个学生,在没有姓名的时候就是一个无意义的对象。

一般而言,无需任何数据就可以建立的类可以包含默认构造函数,而需要数据的类则不可以包含默认构造函数。然而没有默认构造函数的类在实现上会有很多限制:

限制一:创建数组

如一个类没有默认构造函数,在创建数组的时候没有一种对数组所有元素赋参数的方法,因此数组将创建失败。

对于这个限制,有几个办法可以回避

  • 回避方法一:对于非堆上的数组,可以在创建时提供必要的参数:
Student list[] = { Student(Bruce), Student(Steven)};

然而这个方法的缺点很明显,它只适用于非堆数组。

  • 回避方法二:使用 typedef 定义一个指针
typedef Student* Stup;
StuP myStu[10];
Stup * list = new StuP[10];

定义完毕后再对每一个元素进行赋值。

但这个方法存在两个缺点,一是在删除数组的时候需要释放数组中所有元素的内存,否则会引起程序内存泄漏;二是这种方法会增加额外的内存空间消耗。

  • 回避方法三:为数组分配 raw memory
void *rawMemory = operator new[](10 * sizeof(Student));
Student* list = static_cast<Student*>(rawMemory);

然后再对所有元素赋参数。同样的删除数组的时候也需要手动释放内存。

这个方法最大的缺点是大多数程序员并不知道这种用法,这将增加维护成本。

限制二:无法许多基于模版的容器类中使用。

在实例化一个模版类的时候,模版参数应提供一个无参构造函数,这也是大多数基于模版的容器的要求:

template<class T>
class Array{
public:
    Array(int size);
private:
    T *data;
};

在大多数情况下都可以通过仔细的设计避开这个问题,例如 vector 就是避开了这个问题。

限制三:在设计虚基类的时候,提供或不提供默认构造函数的问题

若基类不提供默认构造函数,就使得派生类难以实现,因为派生类必须为虚基类提供所有参数,同时也要求派生类需要了解所有虚基类的参数的含义。

但另一方面,如果提供默认构造函数就会使得对象有“无意义”的可能。如果成员在大多数情况下都无意义,那么就有必要对成员进行检测,这会影响程序的效率。

小结:

在可能使得对象没意义的情况下,尽量避免使用默认构造函数。即使没有无用的默认构造函数的类在实现上会有限制,但是通过仔细的设计可以很大程度避免限制。而没默认构造函数的类可以保证你的对象是有意义,而不再需要对对象进行不必要的检测。