**
印象笔记:
**
default constructor 也就是说不给任何自变量就可调用者。Constructors用来将对象初始化,所以default constructors的意思是在没有任何外来信息的情况将对象初始化。
数值之类的对象可以被合理地初始化为0或一个无意义值。其他诸如指针之类的对象亦可以被合理地初始化为null或无意义值。数据结构如linked lists,maps等可以被初始化为空容器。
但并非所有对象都落入这样的分类。有许多对象,如果没有外来信息,就没有办法执行一个完全的初始化动作。例如,某个通信簿字段的class如果没有获得外界指定的人名,产生出来的对象将毫无意义。在某些公司,所有仪器设备都必须有个识别号码,如果没有获得适当的ID号码,将毫无意义。
考虑以下缺乏default constructor的class:
class EquipmentPrice{
public:
EquipmentPrice(int IDNumber);
}
可能在以下情况下出现问题。
1)在产生对象数组的时候,一般而言没有任何方法可以为数组中的对象指定constructor自变量。
EquipmentPiece bestPieces[10]; // 错误!没有正确调用
// EquipmentPiece 构造函数
EquipmentPiece *bestPieces =
new EquipmentPiece[10]; // 错误!与上面的问题一样
解决方法:
a.使用非堆内存non-heap数组
int ID1, ID2, ID3, ..., ID10; // 存储设备ID号的
// 变量
...
EquipmentPiece bestPieces[] = { // 正确, 提供了构造函数的参数
EquipmentPiece(ID1),
EquipmentPiece(ID2),
EquipmentPiece(ID3),
...,
EquipmentPiece(ID10)
};
b.使用“指针数组”而非“对象数组“
typedef EquipmentPiece* PEP; // PEP 指针指向
//一个EquipmentPiece对象
PEP bestPieces[10]; // 正确, 没有调用构造函数
PEP *bestPieces = new PEP[10]; // 也正确
此法有两个缺点:
**必须记得将此数组所指的所有对象删除;
**需要的内存总量比较大,因为需要一些空间来放置指针,还需要一些空间来放置EquipmentPiece objects。
“过度使用内存”这个问题可以通过先为数组分配raw memory,然后使用“placement new”在这块内存上构造EquipmentPiece objects。
但无论如何,还是必须供给constructor一个自变量,作为每个EquipmentPiece objects的初值。这个“由指针构成数组”的方法只是允许你在“缺乏default constructor”的情况下仍能产生对象数组。
placement new的缺点是在数组内的对象结束生命时,还需要手动调用其destructors,最后还得以调用operator delete[]的方式释放raw memory。
2)class缺少default constructors带来的第二个缺点是:将不适用于许多template-based container classes。
对于templates而言,被实例化的目标类型必须得有一个default constructor。因为在那些templates内几乎总是会产生一个以“template类型参数”作为类型而架构起来的数组。
大部分情况下,谨慎设计template可以消除对default constructor的需求。但大部分都不兼容于(不够严谨的)template。
最后一个考虑点,virtual base classes。virtual base class constructors的自变量必须由欲产生的对象的派生层次最深的class提供。于是要求所有的派生类都要为这个virtual base class的constructor提供自变量。这是设计者不期望也不欣赏的要求。
由于“缺乏default constructor”带来的束缚,有人便认为所有classes都应该提供default constructor–甚至即使其default constructor没有足够信息将对象做完整的初始化。依照这样的哲学,Equipment可能会被修改如下:
class EquipmentPiece {
public:
EquipmentPiece( int IDNumber = UNSPECIFIED);
...
private:
static const int UNSPECIFIED; // ID值不确定。
};
这不再保证一个EquipmentPiece object的所有字段都是富有意义的初值,将会造成大部分member function必须检查字段是否存在。通常,许多编译器选择的解决方法是:什么都不做,仅抛出一个exception,或是调用某函数将程序结束掉。这样的事情一旦发生,我们很难认为软件的整体质量因为画蛇添足地加上了一个default constructor而获得提升。
如果class constructor可以确保对象的所有字段都会被正确地初始化,上述的时间代价、空间代价便都可以免除。如果default constructor无法提供这种保证,那么最好避免让default constructor出现。虽然这可能会对class的使用方式带来某种限制,但同时带来一种保证:当你真的使用了这样的class,你可以预期它们产生的对象会被完全地初始化,实现上亦富有效率。