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

How to best handle copy-swap idiom with uninitialised memory

作为一项学术练习,我创建了一个自定义矢量实现,我想支持非 pod 类型的复制。

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

当我为向量保留内存,然后 push_back 一个元素(它管理它自己的资源并实现了复制和赋值运算符 - 我暂时忽略了移动构造函数)我有一个使用该类型的复制交换习语的问题。

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

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

另一种是在进行交换之前将容器中类型space的内存设置为0(这样临时将为null并且调用析构函数将无错误地运行)。不过,这对我来说有点老套,我不确定是否有更好的选择(请参阅下面的代码以获取示例)您还可以在调用 reserve 后将所有保留 space 设置为 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;
}

任何 information/advice 将不胜感激!

已解决!

感谢@Brian 和@Nim 帮助我理解赋值 (copy/swap) 有效时的用例。

为了达到我想要的效果,我只需要更换线

*customType = customTypeToCopy;

new (customType) CustomType(customTypeToCopy);

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

谢谢!

您没有使用复制和交换进行构造。

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

如果你在做构造而不是赋值——因为目标是未初始化的——复制和交换解决的问题不存在。您只需调用带有新位置的构造函数。如果成功了,太好了。如果它因抛出异常而失败,该语言会保证任何已构造的子对象都被销毁,而您只需让异常向上传播即可;在失败的情况下,目标的状态将与之前相同:未初始化。