我的程序使用一个简单的结构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;
}
答案 0 :(得分:6)
Q1:你能依靠编译器自动生成那些吗?
是(在您的示例中)。请参阅C ++ 11标准(第12节)或文章Implicit Move Won’t Go!(接近结尾的好图表)。总结(和简化),将自动生成以下所有特殊成员函数(隐式声明并定义为默认):
(我使用丑陋的首字母只是为了使每个列表项保持一行。)除了上面的“因果”之外,对于除了析构函数之外的所有内容,还有一个额外的约束,即生成的默认值必须有意义,即所有数据成员必须是可复制的(对于CC和CAO)或可移动的(对于MC和MAO)。 (实际上,准确的规则有点复杂,但我不想在这里重新标注。)
Q2:自动生成的功能是否正确?
是(在您的示例中)。所有数据成员(此处为普通int
)都具有正确的复制/移动语义(他们的复制/移动构造函数和赋值运算符做正确的事情,以及为{{1}自动生成的那些会调用它们。)
问题3:无论如何,你应该手动定义它们吗?
我认为没有优势(在您的示例中)和潜在问题(如您的示例所示,请参阅评论)。
答案 1 :(得分:1)
您可以在编译器上进行中继,因为您的数据成员是内置类型,因此可以通过成员副本创建对象的副本。如果您不定义复制/移动构造函数或赋值运算符,则会出现这种情况。如果您有一个指针作为数据成员,则可能需要它们。
答案 2 :(得分:1)
你不需要为显式复制构造函数编写一个赋值,因为你的struct Rect的成员都是c ++ buildin类型,所以编译器会生成一个按位复制构造函数。在你的代码中,我认为你可以依赖你的编译器完全。
答案 3 :(得分: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;
};
还要注意默认构造函数重用完整的构造函数,这要归功于委托构造函数