4

我的程序使用一个简单的结构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;
    }
4

4 回答 4

6

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

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

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

(我使用丑陋的首字母只是为了让列表项各占一行。)除了上面的“因为”之外,对于除析构函数之外的所有内容,还有一个额外的约束,即生成的默认值必须是有意义的,即所有数据成员必须是可复制(对于 CC 和 CAO)或可移动(对于 MC 和 MAO)。(实际上精确的规则有点复杂,但我不想在这里重新表述标准。)

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

是的(在你的例子中)。您的所有数据成员(此处为 plain int)都具有正确的复制/移动语义(它们的复制/移动构造函数和赋值运算符做正确的事情,而那些自动生成的Rect将调用它们)。

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

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

于 2013-08-31T13:00:28.287 回答
1

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

于 2013-08-31T12:18:11.963 回答
1

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

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

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

于 2013-08-31T12:52:15.833 回答
1

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

于 2013-08-31T12:12:06.177 回答