复制构造函数替换移动构造函数

Copy constructor replace Move constructor?

本文关键字:构造函数 移动 替换 复制      更新时间:2023-10-16

我从链接学习移动语义

我有一个类

class Holder
{
public:
    Holder(int size)         // Constructor
    {
        m_data = new int[size];
        m_size = size;
    }
    ~Holder()                // Destructor
    {
        delete[] m_data;
    }
    Holder(const Holder& other)
    {
        cout << "copy constructor" << endl;
        m_data = new int[other.m_size];
        memcpy(m_data, other.m_data, sizeof(int));
        m_size = other.m_size;
    }
    Holder &operator=(const Holder& other)
    {
        if (this == &other)
            return *this;
        delete[]m_data;
        m_data = new int[other.m_size];
        memcpy(m_data, other.m_data, sizeof(int));
        m_size = other.m_size;
        return *this;  
    }
private:
    int*   m_data;
    size_t m_size;
};

此类具有复制构造函数,例如:

    Holder(const Holder& other)
    {
        cout << "copy constructor" << endl;
        m_data = new int[other.m_size];
        memcpy(m_data, other.m_data, sizeof(int));
        m_size = other.m_size;
    }

然后移动构造函数已实现:

Holder(Holder&& other)     // <-- rvalue reference in input
{
  m_data = other.m_data;   // (1)
  m_size = other.m_size;
  other.m_data = nullptr;  // (2)
  other.m_size = 0;
}

我有一个问题:为什么我们不以下内容实现复制构造函数:

Holder( Holder& other)
{
    m_data = other.m_data;
    m_size = other.m_size;
    other.m_data = nullptr;
    other.m_size = 0;
}

您能告诉我为什么这种方式不使用吗?谢谢

您对该"复制构造函数"的实现在语义上是副本构造函数和移动构造函数之间的某些内容,这将令人困惑,然后危险。如果您的意图是始终移动并且永远无法复制对象,那么您可以强迫该类对此不可复制:

Holder(const Holder& other) = delete;
Holder& operator=( const Holder& ) = delete;

即使您遵循默认复制构造函数和默认分配的编译器的非生成规则,明确删除这些方法也更加清楚。

我猜出于安全原因。尽管您所描述的内容是可以的(并且在知道一个人在做什么时正确(,但是这样做可能最终会带有一个看起来很满但实际上是空的对象。

在一般移动中,构造函数对于使用以下临时值的使用更有用:

Holder a_function(...){...}

然后可用于构造:

Holder object(a_function(...));

或在执行以下操作时避免重新分配/复制大量数据/内存:

Holder object(Holder(100));

,但特别是对于没有默认构造函数的这种情况(一般而言,每个对象都应该在构造后满足(,通过将复制构造函数作为您的建议(类似于移动构造函数(,然后通过:

Holder object1(100);
Holder object2(object1);

一个人最终会以object1的形式看起来像普通对象,但它是空的。因此,它可能会成为bug/s后来的来源。

尽管很明显,但我应该补充一点,毁坏对象没有问题。只是,如果没有某种类型的安全守卫(边界检查(,则不知道它的空虚,很可能会导致非法内存访问。

我有一个问题:为什么我们不以下内容实现复制构造函数 :

Holder( Holder& other)
{
    m_data = other.m_data;
    m_size = other.m_size;
    other.m_data = nullptr;
    other.m_size = 0;
}

您能告诉我为什么这种方式不使用吗?

上面的代码不是从其他代码复制状态,而是将其他资源转移到创建的当前对象。

来自移动构造函数

移动构造函数通常"窃取"该论点持有的资源 (例如,指向动态分配的对象,文件描述符,TCP 插座,I/O流,运行线程等(而不是制作副本 他们,并将论点留在某些有效但其他方面 不确定状态。

Holder A;
Holder B(std::move(A));  
// B is created by calling move constructor    
// Resources held by A are transferred to B  (ref1)
Holder C;
Holder D(C);             
//C is created by calling copy constructor
//state or resources of C, D are same and C can be used after this
//Object C usable

ref1