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

<think>嗯,用户遇到了一个C++的问题,说是'Cps::MessageCpsMode'没有合适的默认构造函数。这个问题看起来和类的构造函数有关。让我先回忆一下C++中构造函数的相关知识。 首先,默认构造函数是指需要参数就可以调用的构造函数。如果用户没有显式定义任何构造函数,编译器会自动生成一个默认构造函数。但如果用户定义了其他构造函数,比如带参数的构造函数,编译器就会再生成默认构造函数了,这时候如果代码中尝试无参构造对象,就会报错。比如引用[1]提到,如果有自定义构造函数,就会有默认构造函数。这可能就是用户遇到的问题所在。 用户提到的类名是Cps::MessageCpsMode,可能这个类里面定义了一个带参数的构造函数,但没有定义默认构造函数。当在某个地方试图创建这个类的实例而没有提供参数时,就会报错。比如在创建对象数组、或者作为容器元素默认初始化的时候,需要默认构造函数。 解决方案的话,可能有几个方向。首先,检查类的定义,看看是否确实没有默认构造函数。如果用户自己定义了带参数的构造函数,那么需要显式添加一个默认构造函数。或者,如果有C++11支持,可以用= default来让编译器生成,如引用[2]中的例子,使用MyStruct() = default; 这样可以保留默认构造函数。 另外,引用[3]中的拷贝构造函数的情况,但用户的问题可能和拷贝构造函数无关,除非在特定情况下触发了拷贝构造,但更可能是默认构造的问题。引用[4]提到结构体如果没有显式构造函数,编译器会生成默认的,但如果是类的话,如果用户定义过其他构造函数,就会自动生成了。 所以可能的解决步骤是:首先检查MessageCpsMode类的定义,看是否有自定义的构造函数,特别是带参数的,而没有默认构造函数。然后,根据情况添加默认构造函数,或者调整实例化的代码,提供必要的参数。 用户可能需要检查在哪些地方使用了MessageCpsMode的无参构造,比如声明对象时没有初始化,或者在容器中使用时没有正确初始化。比如,如果有一个vector<MessageCpsMode>,而MessageCpsMode没有默认构造函数,那么在resize或者默认构造元素时会出错。这时候可能需要用emplace_back或者提供初始化参数。 另外,如果无法修改类的定义,可能需要检查是否有必要添加默认构造函数,或者是否所有实例化的地方都能提供必要的参数。比如,如果类有一个带参数的构造函数,可以修改实例化代码,传入需要的参数,而是依赖默认构造。 总结可能的解决方案:1. 添加默认构造函数;2. 使用C++11的=default;3. 检查实例化代码,确保提供参数;4. 使用初始化列表或者在容器中正确构造对象。 还需要注意,如果类中有成员变量没有默认构造函数,也可能导致问题。例如,如果MessageCpsMode的某个成员类没有默认构造函数,而MessageCpsMode自己没有在初始化列表中初始化该成员,也会导致编译错误。这时候需要在MessageCpsMode的构造函数中显式初始化该成员。 可能用户的情况是,MessageCpsMode有一个自定义的构造函数,但没有默认构造函数,而代码中某个地方试图默认构造它,导致错误。这时候需要根据具体情况,选择添加默认构造函数,或者在创建对象时传入参数。</think>针对"C++中'Cps::MessageCpsMode'缺少默认构造函数"的问题,以下是分步解决方案: ### 1. 理解错误根源 当出现`no appropriate default constructor available`错误时,表示编译器无法找到无参数的构造函数,但代码中存在隐式调用默认构造函数的情况[^1][^4]。常见场景包括: - 创建对象数组时:`Cps::MessageCpsMode arr[5];` - 容器未初始化元素时:`std::vector<Cps::MessageCpsMode> vec(10);` - 类成员未显式初始化时(若成员类型缺少默认构造函数) ### 2. 检查类定义 假设`Cps::MessageCpsMode`类定义如下: ```cpp class MessageCpsMode { public: MessageCpsMode(int param); // 自定义构造函数 // 没有默认构造函数 }; ``` 此时任何无参对象创建都会报错。 ### 3. 解决方案 #### 方案一:添加默认构造函数 ```cpp class MessageCpsMode { public: MessageCpsMode() = default; // C++11特性[^2] MessageCpsMode(int param); }; ``` 或显式定义: ```cpp class MessageCpsMode { public: MessageCpsMode() {} // 空实现 MessageCpsMode(int param); }; ``` #### 方案二:修改实例化代码 ```cpp // 原始错误代码 Cps::MessageCpsMode obj; // 修改为带参数构造 Cps::MessageCpsMode obj(123); // 提供必要参数 ``` #### 方案三:容器初始化适配 ```cpp std::vector<Cps::MessageCpsMode> vec; vec.reserve(10); // 预分配空间但构造 vec.emplace_back(123); // 使用参数直接构造[^3] ``` #### 方案四:成员初始化处理 若类包含无默认构造的成员: ```cpp class Container { MessageCpsMode member; // 成员对象 public: Container() : member(123) {} // 必须显式初始化[^1] }; ``` ### 4. 特殊情况处理 若类设计上**允许默认构造**,应通过以下方式阻止隐式构造: ```cpp class MessageCpsMode { public: MessageCpsMode() = delete; // C++11特性 MessageCpsMode(int param); }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值