2015-12-02 40 views
1

作为学术练习,我创建了一个自定义向量实现,我希望支持复制非pod类型。如何最好地处理未初始化内存的可交换成语

我希望容器支持不提供默认构造函数的存储元素。

当我为向量保留内存,然后push_back一个元素(它管理它自己的资源并且实现了复制和赋值操作符 - 我忽略了当前的构造函数)我有一个使用copy-交换该类型的成语。

由于交换发生在仍为未初始化内存的类型上,所以在交换之后,被临时调用的析构函数将尝试释放某些未初始化的数据,这当然会激化。

我可以看到几种可能的解决方案。一种是确保所有非pod类型都实现默认构造函数,并在集合中的每个元素上调用该元素(放置新元素)。我不是这个想法的粉丝,因为它看起来既浪费又麻烦。

另一种方法是在执行交换之前将容器中类型空间的内存mem设置为0(这样临时值将为空,调用析构函数将无误地运行)。这对我来说有点不好意思,我不确定是否有更好的选择(请参阅下面的代码以获得一个示例)。您也可以在为一堆元素调用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); 

调用拷贝构造函数没有赋值操作符!

谢谢!

+0

我已经读了几次这个问题(可能是迟到了),但不清楚你是否有执行交换的自定义类型*或矢量*的问题? (顺便说一句,我不认为'memset()'会起作用)并且不清楚在单元化内存中使用copy-swap的原因(意味着你试图访问一个超出vector的“bounds”范围的元素...) – Nim

+0

嘿Nim!对不起,如果我的解释不是很好,我会尽力澄清。基本上,在交换时,位于未初始化内存值的info_指针会被移入临时进入该函数的临时位置,然后当作用域结束时,该临时对象会调用析构函数,但info_指针只是垃圾,所以调用delete []会导致崩溃。如果我以前memset内存,它将为空,并且调用delete不起作用。我只想知道这是一件好事还是坏习惯。我想不出一个好的选择:( – Tom

+0

...但这意味着你*交换*的元素是*出界*(即超出矢量的*大小*) – Nim

回答

1

您不使用复制和交换进行施工。

您可以使用copy-and-swap进行赋值,以解决以下问题:赋值的左侧是一个已经初始化的对象,因此需要在复制右侧的状态之前释放其占用的资源或进入它;但如果通过抛出异常复制或移动构造失败,我们希望保持原始状态。

如果您正在进行构建而不是分配---因为目标未初始化---通过复制和交换解决的问题不存在。您只需使用新的位置调用构造函数。如果成功,那很好。如果抛出异常失败,语言保证已经构建的任何子对象被销毁,并且你只是让异常向上传播;在失败情况下,目标的状态将与以前相同:未初始化。

+0

啊好吧我看到了 - 所以我的问题就变成了如果该对象没有默认构造函数?我认为我需要在该元素上调用新的位置,但是如果我想调用特定的构造函数。有没有办法做到这一点?当你调用reserve和push_back元素时,std :: vector如何处理这种情况?非常感谢您的回答! – Tom

+0

@Tom'vector :: push_back'使用复制或移动构造函数。如果这个类型不能被复制或移动,你不能拥有它的一个向量(或者至少你不能'push_back')。 – Brian

+0

好吧,我明白了,谢谢Brian!我有我的解决方案! :)如果我理解正确,我只需要更新代码,而不是做分配,我这样做 - 新(customType)CustomType(customTypeToCopy);非常感谢你!! :) – Tom

相关问题