作为一项学术练习,我创建了一个自定义矢量实现,我想支持复制非pod类型。
我希望容器支持存储不提供默认构造函数的元素。
当我为向量保留内存,然后push_back一个元素(管理它自己的资源并实现了一个复制和赋值操作符 - 我暂时忽略了移动构造函数)我有一个使用该类型的复制交换习语的问题。
因为交换发生在仍然未初始化的内存的类型上,在交换之后,为临时调用的析构函数将尝试释放一些未初始化的数据,这当然会爆炸。
我可以看到一些可能的解决方案。一个是确保所有非pod类型实现默认构造函数,并在集合中的每个元素上调用(placement new)。我不是这个想法的粉丝,因为它看起来既浪费又麻烦。
另一种方法是在进行交换之前将容器中类型空间的内存设置为0(这样临时将为null,并且调用析构函数将无错误地运行)。这对我来说有点不好意思,我不确定是否有更好的替代方案(请参阅下面的代码以获得此示例)您也可以在调用一堆保留空间后将所有保留空间memset设置为0元素,但这可能是浪费。
是否有关于如何为std :: vector实现此文档的文档,因为调用reserve将不会为已分配元素调用构造函数,而resize将会(对于未实现默认构造函数的类型,可以将构造临时文件作为第二个参数传递)致电)
下面是您可以运行的一些代码来演示问题,我省略了实际的矢量代码,但原理保持不变。
#include <iostream>
#include <cstring>
// Dumb example type - not something to ever use
class CustomType {
public:
CustomType(const char* info) {
size_t len = strlen(info) + 1;
info_ = new char[len];
for (int i = 0; i < len; ++i) {
info_[i] = info[i];
}
}
CustomType(const CustomType& customType) {
size_t len = strlen(customType.info_) + 1;
info_ = new char[len];
for (int i = 0; i < len; ++i) {
info_[i] = customType.info_[i];
}
}
CustomType& operator=(CustomType customType) {
swap(*this, customType);
return *this;
}
void swap(CustomType& lhs, CustomType& rhs) {
std::swap(lhs.info_, rhs.info_);
}
~CustomType() {
delete[] info_;
}
char* info_;
};
int main() {
CustomType customTypeToCopy("Test");
// Mimics one element in the array - uninitialised memory
char* mem = (char*)malloc(sizeof(CustomType));
// Cast to correct type (would be T for array element)
CustomType* customType = (CustomType*)mem;
// If memory is cleared, delete[] of null has no effect - all good
memset(mem, 0, sizeof(CustomType));
// If the above line is commented out, you get malloc error - pointer
// being freed, was not allocated
// Invokes assignment operator and copy/swap idiom
*customType = customTypeToCopy;
printf("%s\n", customType->info_);
printf("%s\n", customTypeToCopy.info_);
return 0;
}
非常感谢任何信息/建议!
解决!
感谢@Brian和@Nim帮助我理解赋值(复制/交换)有效的用例。
要达到我想要的效果,我只需要更换一行
*customType = customTypeToCopy;
与
new (customType) CustomType(customTypeToCopy);
调用复制构造函数而不是赋值运算符!
谢谢!
答案 0 :(得分:1)
您不能使用复制和交换进行构建。
您使用复制和交换进行分配以解决以下问题:分配的左侧是已初始化的对象,因此在拥有右侧之前需要释放它所拥有的资源。 s状态被复制或移入其中;但如果复制或移动构造因抛出异常而失败,我们希望保持原始状态。
如果你正在进行建设而不是分配---因为目标是未初始化的 - 通过复制和交换解决的问题并不存在。您只需使用placement new调用构造函数。如果成功,那很好。如果它通过抛出异常而失败,则该语言保证已经构造的任何子对象都被销毁,并且您只是让异常向上传播;在失败的情况下,目标的状态将与以前相同:未初始化。