缺省构造函数即没有参数或所有参数都声明了默认值的构造函数,可以在没有任何外部数据下初始化对象。当类没有声明构造函数,编译器会提供一个隐式缺省构造函数。
借由缺省构造函数创建的对象,其成员变量被初始化为固定的或是不确定的值,不能保证类的成员都被正确初始化。当类有一个无意义的缺省构造函数,未能将所有部分正确初始化,类的成员函数则必须另外测试成员变量是否是有效的,付出了更多的代码,因此应尽量避免。
没有缺省构造函数的类必须传入外部数据来构造,让人相信这个类的对象会被正确的实现,但也存在使用上的限制。
对象数组
直接声明对象数组,会调用类的缺省构造函数,如果没有则必须为数组初始化传参。
Class array[10];//error: no matching function for call to "Class::Class()"
Class array[10] = {{val1, val2, ...}, ...};//correct
但对于堆分配的对象数组(new []
),并不能通过上述方式传参。
Class * array = new Class[2];//error: no matching function for call to "Class::Class()"
Class * array = new Class(value)[2];//error: 语法错误
可以通过(类)指针数组来替代对象数组,并逐个为指针分配堆内存。对应的,在释放时也需要逐个调用析构函数。
Class * array[10];
for(auto it : array)
{
it = new Class(value);
}
指针数组的方式使用了额外的内存保存指针,可以使用placement new
方法为数组分配raw memory
,在内存中构造对象。
int size = 10;
void * memory = operator new[](size * sizeof(Class));//类似malloc返回未处理的堆内存块地址
Class * array = static_cast<Class *>(memory);
for(int i=0; i<size; ++i)
{
new (&array[i]) Class(1, 2);//在已分配的地址上构造对象
}
释放时以相反的顺序首先逐个调用析构函数,最后释放内存。
for(int i=0; i<size; ++i)
{
array[i].~Class();
}
memory = static_cast<void *>(array);
operator delete[] (memory);
在模板容器中使用
标准vector
没有要求参数类型必须有缺省构造函数,但并不是所有容器都如此。没有缺省构造函数的类在模板中的兼容受到限制。
pair<int, Class> pa;//error: no matching function for call to ‘std::pair<int, Class>::pair()‘
虚基类
虚基类是否提供缺省构造函数是一个两难的决定。如果虚基类没有缺省构造函数,几乎所有的派生链在实例化时都必须给它的构造函数提供参数,那么无论派生多远都需要了解这些参数的意义才能正确初始化。如果虚基类提供缺省构造函数,就必须为安全性做保证,花费额外的代码去测试是否正确初始化。