c++ 11移动赋值和复制赋值(操作符)- delete的使用



我目前的理解是 c++ 11的移动和复制赋值操作符都应该调用delete来防止内存泄漏,但是c++ 11的移动和复制构造函数不应该

如果我的理解是正确的,构造函数不需要调用delete,但是我不确定为什么。考虑下面的例子:

class my_class
{
    my_class(int num_data) : my_data{new double[num_data]}, my_data_length{num_data}
    {
    }
    // This class manages a resource
    double *my_data;
    int my_data_length;
}
// Big 4 go here - see wikipedia example below.
my_class a(10);
my_class b(10);
my_class c(10);
a = b; // Need to delete old storage contained in a before reallocating
a(c); // For some reason we don't need to delete the old storage here? I find this puzzling

查看这篇维基百科文章中的示例代码,我很清楚:

  • Move构造函数不会因为资源被传输而泄漏。类所指向的任何已分配的数据,如果不在到期,则被转移到即将到期的类中,并由即将到期的类的析构函数删除。

  • 我很困惑,是否复制构造函数泄漏,然而。

  • 移动赋值操作符可能不会泄漏,因为它只是交换指针。

  • 我又被拷贝赋值操作符弄糊涂了。我不确定为什么需要使用临时对象?我的猜测是tmpother拥有的资源在这个函数结束时超出范围时被破坏?(除了tmp的资源与this类中的指针交换?)

为方便起见,下面提供了其中的代码:

#include <cstring>
#include <iostream>
class Foo
{
public:
    /** Default constructor */
    Foo() :
        data (new char[14])
    {
        std::strcpy(data, "Hello, World!");
    }
    /** Copy constructor */
    Foo (const Foo& other) :
        data (new char[std::strlen (other.data) + 1])
    {
        std::strcpy(data, other.data);
    }
    /** Move constructor */
    Foo (Foo&& other) noexcept : /* noexcept needed to enable optimizations in containers */
        data(other.data)
    {
        other.data = nullptr;
    }
    /** Destructor */
    ~Foo() noexcept /* explicitly specified destructors should be annotated noexcept as best-practice */
    {
        delete[] data;
    }
    /** Copy assignment operator */
    Foo& operator= (const Foo& other)
    {
        Foo tmp(other); // re-use copy-constructor
        *this = std::move(tmp); // re-use move-assignment
        return *this;
    }
    /** Move assignment operator */
    Foo& operator= (Foo&& other) noexcept
    {
        // simplified move-constructor that also protects against move-to-self.
        std::swap(data, other.data); // repeat for all elements
        return *this;
    }
private:
    friend std::ostream& operator<< (std::ostream& os, const Foo& foo)
    {
        os << foo.data;
        return os;
    }
    char* data;
};
int main()
{
    const Foo foo;
    std::cout << foo << std::endl;
    return 0;
}

我想这是提示为什么它是重要的设置(未初始化/未分配)悬空指针到nullptr,因为这将防止在一个析构函数删除时的内存错误?

我认为这是因为资源是通过move构造函数传输的,但是即将过期的对象接收到一个从未分配的悬空指针——我们不希望析构函数被调用,delete是指针——除非我们确保它指向nullptr(无操作)。

谁能澄清我提出的一些观点?

move构造函数和赋值操作符将数据指针设置为nullptr,因为指针的所有权正在被移动。如果它们没有,并且在原始和新对象上都调用了delete,那么就会有双重删除。或者如果旧的指针被删除了,新的指针就会有一个无效的指针。同时,复制构造函数和赋值操作符创建了一个完全独立的数据副本。

deletenullptr上是安全的,不做任何事情。

Move构造函数不会因为资源被传输而泄漏。任何未过期的类所指向的已分配数据将被转移到即将过期的类,并由即将过期的类的析构函数删除。

是的,Move构造函数不会因为资源被传输而泄漏。更重要的是,它从不分配自己的资源。

我很困惑,是否复制构造函数泄漏,然而。

复制构造函数保留原始数据,并对数据进行深度复制。因此,两个对象都将清理自己的数据。

Move赋值操作符可能不会泄漏,因为它只是交换指针。

正确的。

我又被拷贝赋值操作符弄糊涂了。我不确定为什么需要使用临时对象?我的猜测是tmp和其他拥有的资源在这个函数结束时超出作用域时被销毁?(除了tmp将其资源与this类中的指针交换?)

复制赋值首先使用复制构造函数创建一个新的副本,然后使用move赋值操作符将新副本移动到对象中,并将旧数据移动到临时对象中。则临时对象超出作用域,调用析构函数并删除旧数据。

我想这是提示为什么它是重要的设置(未初始化/未分配)悬空指针nullptr,因为这将防止在一个析构函数删除时的内存错误?

正确的。

最新更新