【发布时间】:2015-12-02 23:47:57
【问题描述】:
作为一项学术练习,我创建了一个自定义矢量实现,我希望支持复制非 pod 类型。
我希望容器支持存储不提供默认构造函数的元素。
当我为向量保留内存,然后 push_back 一个元素(它管理它自己的资源并实现了复制和赋值运算符 - 我暂时忽略了移动构造函数)时,我在使用复制交换时遇到了问题该类型的成语。
因为交换发生在仍然是未初始化内存的类型上,所以在交换之后,临时调用的析构函数将尝试释放一些未初始化的数据,这当然会爆炸。
我可以看到一些可能的解决方案。一种是确保所有非 pod 类型都实现一个默认构造函数,并在集合中的每个元素上调用它(放置 new)。我不喜欢这个想法,因为它看起来既浪费又麻烦。
另一种方法是在进行交换之前将容器中类型空间的内存设置为 0(这样临时将为 null 并且调用析构函数将正常运行)。不过,这对我来说有点 hacky,我不确定是否有更好的选择(请参阅下面的代码以获取此示例)您也可以在为一堆元素调用 reserve 之后将所有保留空间 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);
调用复制构造函数而不是赋值运算符!
谢谢!
【问题讨论】:
-
我已经读过这个问题几次(可能已经晚了),但不清楚您是否在为 custom type 或矢量? (顺便说一句。我不认为
memset()会起作用)并且尚不清楚具有统一化内存的复制交换在哪里启动(意味着您正在尝试访问向量“边界”之外的元素.. .) -
嘿尼姆!对不起,如果我的解释不是很好,我会尽力澄清。基本上在进行交换时,位于未初始化内存值中的 info_ 指针被移动到我传递给该函数的临时对象中,然后当范围结束时,在该临时对象上调用析构函数,但 info_ 指针只是垃圾,因此调用 delete[] 会导致崩溃。如果我之前memset内存,它会为null,调用delete没有效果。我只是想知道这是一件好事还是不好的做法。我想不出一个好的选择:(
-
...但这意味着您正在 swapping 与一个 out-of-bounds 的元素(即超出 size 的向量..)
-
不,我已经为它分配了空间(参见示例代码)它只是未初始化,所以任何东西都可能在那个空间中,因为没有调用构造函数并且我没有将内存归零(除非我打电话给memset)。您可以将代码复制并粘贴到 ideone 中,看看它在做什么,如果您注释掉 memset 行会发生什么,如果有帮助的话。
-
..我理解-但是您正在建模的是您正在与尚未构造的对象交换的情况-这是无效的。您不能与尚未构造的对象交换(在向量的上下文中,您正在访问其边界之外的元素 - 即尚未构造的元素 - 保留存储不一样作为构造一个对象...)
标签: c++ c++11 memory vector deep-copy