复制构造函数替换Move构造函数?

时间:2019-05-17 08:57:17

标签: c++ c++11 visual-c++

我从link学到了移动语义

我上课

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;
}

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

3 个答案:

答案 0 :(得分:1)

我出于安全考虑。尽管您所描述的内容是允许的(并且当人们知道自己在做什么时是正确的),但这样做可能会导致对象看起来很满,但实际上是空的。

通常,将move构造函数与临时值一起使用时更有用:

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

然后可以将其用于以下构造:

Holder object(a_function(...));

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

Holder object(Holder(100));

但是对于这种情况,特别是没有默认构造函数(因此,通常每个对象在构造之后都应该是满的),方法是将复制构造函数作为您建议的内容(类似于move构造函数),然后执行以下操作:

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

最后将以object1的形式出现,它看起来像是普通对象,但为空。因此,以后可能会成为错误的来源。

尽管很明显,但我应该补充一点,销毁object1不会有问题。只是,如果没有某种类型的安全防护措施(边界检查),则在其生命周期内不使用它会很无聊,这很可能会导致非法内存访问。

答案 1 :(得分:1)

您对“复制构造函数”的实现在语义上介于复制构造函数和move构造函数之间,这会造成混乱,进而带来危险。如果您的意图是始终移动并且永远无法复制对象,则可以使用以下方法将类强制为不可复制:

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

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

答案 2 :(得分:-1)

  

我有一个问题:为什么我们不像下面那样实现复制构造函数   :

Holder( Holder& other)
{
    m_data = other.m_data;
    m_size = other.m_size;

    other.m_data = nullptr;
    other.m_size = 0;
}
     

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

以上代码不是将其他状态复制,而是将其他状态/拥有的资源移动到正在创建的当前对象中。

来自Move constructors

  

移动构造函数通常会“窃取”参数所占用的资源   (例如,指向动态分配对象的指针,文件描述符,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