如何最好地处理具有未初始化内存的复制交换习惯用法



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

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

当我为向量保留内存,然后推送一个元素(它管理自己的资源,并实现了一个复制和赋值运算符——我暂时忽略了move构造函数)时,我在使用该类型的复制交换习惯用法时遇到了问题。

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

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

另一种方法是在进行交换之前,将容器中该类型空间的内存memset为0(这样临时的将为null,调用析构函数将不会出错)。不过,这对我来说有点棘手,我不确定是否有更好的选择(请参阅下面的代码以获取示例)。在为一堆元素调用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("%sn", customType->info_);
    printf("%sn", customTypeToCopy.info_);
    return 0;
}

如有任何信息/建议,我们将不胜感激!

解决了!

感谢@Brian和@Nim帮助我理解分配(复制/交换)有效的用例。

为了实现我想要的,我只需要更换

*customType = customTypeToCopy;

带有

new (customType) CustomType(customTypeToCopy);

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

谢谢!

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

为了解决以下问题,可以使用复制和交换赋值:赋值的左侧是一个已经初始化的对象,因此在将右侧的状态复制或移动到其中之前,它需要释放所拥有的资源;但是,如果复制或移动构造由于抛出异常而失败,我们希望保持原始状态。

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

最新更新