创建移动赋值函数,不断获取"pointer being freed was not allocated"

Creating a move assignment function, keep getting "pointer being freed was not allocated"

我正在尝试创建一个移动分配函数,但我不断得到 "pointer being freed was not allocated"

const MyString& MyString::operator=(MyString&& move){
    cout<< "Move Assignment" << endl;
    if(this != &move){
        delete[] str;
        len = move.len;
        for(int i = 0; i<len;i++){
            str[i] = move.str[i];
        }
        move.str=nullptr;
        move.len = 0;
        return *this;
    }

    return *this;
}

a.out(37068,0x1000b45c0) malloc:* 对象 0x1001025a0 错误:未分配正在释放的指针 a.out(37068,0x1000b45c0) malloc: * 在 malloc_error_break 中设置断点以调试

这个:

delete[] str;

删除 str。但是然后:

str[i] = move.str[i];

str 被删除。所以这是未定义的行为。

反正这不是一招怎么办。移动的重点是避免复制字符串。假设 strchar*,那么正确的实现如下(参数的通用名称是 rhs,意思是 "right hand side"):

MyString& MyString::operator=(MyString&& rhs) noexcept
{
    std::swap(len, rhs.len);
    std::swap(str, rhs.str);
    return *this;
}

再说一次:只有当 str 只是一个指针时,这才是正确的实现。请注意,该实现不会删除任何内容。删除将发生在 rhs 的析构函数中。 noexcept 说明符不是必需的,但由于此实现永远不会抛出异常,因此将其标记为 noexcept 允许编译器进行更多优化。

除了Nikos C.'s

交换不是唯一的解决方案 – 但它是一种非常优雅的解决方案:您保留目标字符串的内存以在源字符串中重复使用。虽然到目前为止还不错,但您可能希望 re-start 在移动后使用空字符串。同样,您不应该删除内存,它非常适合 re-used。所以你只需将长度设置为 0.

但是,那么你需要单独记住,还有多少字符适合内存。但这无论如何都是有用的。想一想,每次追加单个字符时,您是否希望 re-allocate 字符串的内存?

很可能不会。所以你会添加一些额外的内存(例如,如果你 运行 内存不足,则将容量加倍)。全部加在一起:

class MyString
{
    size_t length;
    size_t capacity;
    char* data;
public:
    MyString& operator=(MyString&& other)
    {
        if(&other != this)
        {
            std::swap(data, other.data);         // as in Nikos' answer
            std::swap(capacity, other.capacity);
            length = other.length;
            other.length = 0;                    // restart with empty string
                                                 // still you have quite a bit of
                                                 // memory already reserved
        }
        return *this;
    }
};

请注意,这是 可选的 ,但实际上,您可能会让人们为他们可能不需要的东西付费 – 如果他们不重复使用从中移出的对象。 ..