【问题标题】:Which copy/move constructor/operator to define for simple structures?为简单结构定义哪个复制/移动构造函数/操作符?
【发布时间】:2013-09-04 01:42:33
【问题描述】:

我的程序使用了一个简单的结构Rect,它被定义为

struct Rect {
    int x1, y1, x2, y2;

    Rect()
    : x1(0), y1(0), x2(0), y2(0) { }

    Rect(int x1, int y1, int x2, int y2)
    : x1(x1), y1(y1), x2(x2), y2(y2) { }
};

我应该定义一个复制/移动构造函数还是一个赋值运算符,或者我可以依靠编译器来自动生成它们吗?问题在于速度和使用原因(例如,移动构造函数会影响程序执行速度)。

构造函数和操作符都是非常重复的工作,如果能依赖编译器自动生成就好了。

    Rect(const Rect& r)
    : x1(r.x1), y1(r.y1), x2(r.x2), y2(r.y2) { }

    Rect(Rect&& r)
    : x1(r.x1), y1(r.y1), x2(r.x2), y2(r.y2) { }

    Rect& operator = (const Rect& r) {
        x1 = r.x1;
        y1 = r.y1;
        x2 = r.x2;
        y2 = r.y2;
    }

【问题讨论】:

  • 你应该让编译器生成的构造函数和赋值运算符来完成它们的工作,因为你的类不需要做任何资源管理。但是在您的情况下,移动构造函数或赋值运算符没有任何好处。
  • 因为你的类没有任何指针(这可能导致潜在的副作用),看起来默认的应该没问题。
  • 你应该完全让编译器来做。它保证是正确的,而您的手动代码不是:例如,operator = 应该是 return *this;

标签: c++ c++11 copy-constructor move-semantics assignment-operator


【解决方案1】:

Q1:你能依靠编译器自动生成这些吗?

是的(在您的示例中)。请参阅 C++11 标准(第 12 条)或文章 Implicit Move Won’t Go!(接近结尾的漂亮图表)。总结(和简化),以下所有特殊成员函数都将自动生成(隐式声明并定义为默认值):

  • 析构函数——因为你没有声明它。
  • 复制构造函数——因为你没有声明它,也没有声明 MC 和 MAO。
  • 复制赋值运算符 - 因为你没有声明它,也没有声明 MC 和 MAO。
  • Move Constructor——因为你没有声明它,也没有声明 D、CC、CAO 和 MAO。
  • 移动赋值运算符——因为你没有声明它,也没有声明 D、CC、CAO 和 MC。

(我使用丑陋的首字母只是为了将列表项保持一行。)除了上面的“因为”之外,除了析构函数之外,还有一个额外的约束,即生成的默认值必须有意义 em>,即所有数据成员必须是可复制的(对于 CC 和 CAO)或可移动的(对于 MC 和 MAO)。 (其实具体的规则要复杂一些,这里不想重述标准。)

Q2:自动生成的函数是否正确?

是的(在您的示例中)。您的所有数据成员(这里是纯 ints)都具有正确的复制/移动语义(他们的复制/移动构造函数和赋值运算符做正确的事情,而为 Rect 自动生成的那些将调用他们)。

Q3:无论如何,你应该手动定义它们吗?

我认为它没有任何优势(在您的示例中)和潜在问题(如在您的示例中,请参阅 cmets)。

【讨论】:

  • 关于Q3:即使没有您希望实现的特定语义,您也不妨定义那些;例如,我通常在基类的protected 部分定义复制和移动操作,以防止意外的对象切片。在这些情况下,最好使用= default 定义。
  • @MatthieuM。 = default 确实很重要(因此您仍然不需要编写它们的主体;在您的情况下,它只是将它们的可访问性从默认的 public 更改为较小)。我认为这方面的标准是他们成为 user-declared 但不是 user-provided
【解决方案2】:

您可以在编译器上进行中继,因为您的数据成员是内置类型,因此可以通过成员复制创建对象的副本。如果您没有定义复制/移动构造函数或赋值运算符,就会出现这种情况。如果您有一个指针作为您的数据成员,您可能需要它们。

【讨论】:

  • 这有点误导。可以得出结论,它仅适用于成员是内置的,但这也适用于设计良好的用户定义类型。
【解决方案3】:

你不需要为你的struct Rect的成员都是c ++内置类型编写一个显式的复制构造函数,所以编译器会生成一个按位复制构造函数。在你的代码中,我认为你可以依赖你的编译器完全。

【讨论】:

  • 查看我对@cpp 回答的评论:它不限于内置类型和按位复制。
【解决方案4】:

如果由于某些原因你想变得冗长,你可以将它们声明为默认为自我记录,如下所示:

struct Rect {
    int x1, y1, x2, y2;

    Rect () : Rect( 0, 0, 0, 0 ) { }

    Rect ( int x1, int y1, int x2, int y2 )
    : x1(x1), y1(y1), x2(x2), y2(y2) { }

    // Rect is Copy/Move Constructible and Assignable
    Rect( Rect const & ) = default;
    Rect( Rect && ) = default;
    Rect& operator= ( Rect const & ) = default;
    Rect& operator= ( Rect && ) = default;
};

还注意到由于委托构造函数,默认构造函数重用了完整的构造函数

【讨论】:

    猜你喜欢
    • 2011-03-08
    • 2018-09-23
    • 2013-11-05
    • 2011-11-15
    • 2021-05-14
    • 1970-01-01
    • 2018-09-17
    • 1970-01-01
    • 2017-06-11
    相关资源
    最近更新 更多