如何最好地处理未初始化内存的复制交换习语

时间:2015-12-02 23:47:57

标签: c++ c++11 memory vector deep-copy

作为一项学术练习,我创建了一个自定义矢量实现,我想支持复制非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);

调用复制构造函数而不是赋值运算符!

谢谢!

1 个答案:

答案 0 :(得分:1)

您不能使用复制和交换进行构建。

您使用复制和交换进行分配以解决以下问题:分配的左侧是已初始化的对象,因此在拥有右侧之前需要释放它所拥有的资源。 s状态被复制或移入其中;但如果复制或移动构造因抛出异常而失败,我们希望保持原始状态。

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