这不是Implementing the copy constructor in terms of operator=的重复,而是一个更具体的问题。 (或者我喜欢思考。)
简介
鉴于这样的(假设的)课程:
struct FooBar {
long id;
double valX;
double valZ;
long valN;
bool flag;
NonCopyable implementation_detail; // cannot and must not be copied
// ...
};
我们无法通过默认生成的函数复制它,因为您既不能复制构造也不能复制NonCopyable对象。但是,该对象的这一部分是我们实际上对复制不感兴趣的实现细节。
对于编写交换函数也没有任何意义,因为交换函数可以复制std :: swap的作用(减去NonCopyable)。
因此,如果我们想要复制这些对象,我们就会自己实现copy-ctor和copy-operator。只需指定其他成员即可轻松完成。
问题
如果我们需要实现copy ctor和operator,我们是应该根据copy运算符实现copy ctor,还是应该用初始化列表“复制”代码?
即,给定:
FooBar& operator=(FooBar const& rhs) {
// no self assignment check necessary
id = rhs.id;
valX = rhs.valX;
valZ = rhs.valZ;
valN = rhs.valN;
flag = rhs.flag;
// don't copy implementation_detail
return *this;
}
我们应该写一个)
FooBar(FooBar const& rhs) {
*this = rhs;
}
或b)
FooBar(FooBar const& rhs)
: id(rhs.id)
, valX(rhs.valX)
, valZ(rhs.valZ)
, valN(rhs.valN)
, flag(rhs.flag)
// don't copy implementation_detail
{ }
答案的可能方面是性能与可维护性和可读性。
答案 0 :(得分:5)
通常你会根据复制构造函数(@Roger Pate的版本)实现赋值运算符:
FooBar& operator=(FooBar copy) { swap(*this, copy); return *this; }
friend void swap(FooBar &a, FooBar &b) {/*...*/}
这需要提供一个swap
函数来交换相关成员(除了implementation_detail
之外的所有成员)。
如果swap
没有抛出此方法,则保证对象不会处于某种不一致状态(仅指定部分成员)。
但是在你的情况下,既然复制构造函数也没有赋值运算符可以在赋值运算符(a)方面抛出实现拷贝构造函数也很好并且更易于维护,那么在两个地方都有几乎相同的代码(b)。
答案 1 :(得分:1)
一般来说,我更喜欢b)而不是a)因为它明确地避免了成员的任何默认构造。对于不是考虑因素的int,double等,但它可以用于具有昂贵操作或副作用的成员。如果您在添加和删除成员时不必考虑此潜在成本/问题,则更易于维护。 Initialiser列表还支持引用和非default-constructable元素。
或者,您可以拥有非“实现细节”成员的子结构,并让编译器生成复制代码,如下所示:
struct X
{
struct I
{
int x_;
int y_;
} i_;
int z_;
X() { }
X(const X& rhs)
: i_(rhs.i_), z_(0) // implementation not copied
{ }
X& operator=(const X& rhs)
{
i_ = rhs.i_;
return *this;
}
};
答案 2 :(得分:1)
如果您对复制std :: swap感到困扰,为什么不将实现细节以外的所有内容放入结构中?
struct FooBarCore {
long id;
double valX;
double valZ;
long valN;
bool flag;
// ...
};
struct FooBar {
FooBarCore core_;
NonCopyable implementation_detail; // cannot and must not be copied
};
然后你可以在FooBar
的复制函数中使用std :: swap作为这个结构。
FooBar& operator=(const FooBar &src) {
FooBarCore temp(src.core_)
swap(temp,*this.core_);
return *this;
}
答案 3 :(得分:1)
好的,另一次尝试,基于我对this answer的评论。
将implementation_detail包装在可复制的类中:
class ImplementationDetail
{
public:
ImplementationDetail() {}
ImplementationDetail(const ImplementationDetail&) {}
ImplementationDetail& operator=(const ImplementationDetail&) {}
public: // To make the example short
Uncopyable implementation_detail;
};
并在你的FooBar中使用这个类。为Foobar默认生成的复制构造函数和复制赋值运算符将正常工作。
也许它甚至可能来自Uncopyable,因此您不会在代码中获得implementation_detail.implementation_detail
。或者,如果您将代码控制到implementation_detail类,只需添加空的Copy Constructor并清空Assignment Operator。
答案 4 :(得分:0)
如果复制构造函数不需要复制implementation_detail并且仍然是正确的(我怀疑后者,但我们暂时假设它),implementation_detail是多余的。
所以解决方案似乎是:使implementation_detail成为静态并依赖于默认生成的Copy Constructor和Assignment Operator。