条款4:非必要不提供default constructor

**
印象笔记:
**

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,你可以预期它们产生的对象会被完全地初始化,实现上亦富有效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值