Raf*_*ini 9 c++ class copy-constructor rule-of-three copy-and-swap
我正在学习c ++,最近我学习了(这里是堆栈溢出)有关复制和交换的习惯用法,我对它有一些问题.所以,假设我有以下类使用复制和交换习惯用法,例如:
class Foo {
private:
int * foo;
int size;
public:
Foo(size_t size) : size(size) { foo = new int[size](); }
~Foo(){delete foo;}
Foo(Foo const& other){
size = other.size;
foo = new int[size];
copy(other.foo, other.foo + size, foo);
}
void swap(Foo& other) {
std::swap(foo, other.foo);
std::swap(size, other.size);
}
Foo& operator=(Foo g) {
g.swap(*this);
return *this;
}
int& operator[] (const int idx) {return foo[idx];}
};
Run Code Online (Sandbox Code Playgroud)
我的问题是,假设我有另一个类,它有一个Foo对象作为数据但没有指针或其他可能需要自定义复制或赋值的资源:
class Bar {
private:
Foo bar;
public:
Bar(Foo foo) : bar(foo) {};
~Bar(){};
Bar(Bar const& other) : bar(other.bar) {};
Bar& operator=(Bar other) {bar = other.bar;}
};
Run Code Online (Sandbox Code Playgroud)
现在我有一系列问题:
上面为Bar类实现的方法和构造函数是否安全?使用复制和交换后,Foo我确保在分配或复制时不会造成任何伤害Bar?
在复制构造函数和swap中通过引用传递参数是必需的吗?
是否正确地说当operator=通过值传递参数时,为此参数调用复制构造函数以生成对象的临时副本,然后将此副本与之交换*this?如果我通过参考通过operator=我会有一个大问题,对吗?
是否存在这种习惯用法无法在复制和分配时提供完全安全的情况Foo?
您应该尽可能在初始化列表中初始化类的成员.这也将照顾我在评论中告诉你的错误.考虑到这一点,您的代码变为:
class Foo {
private:
int size;
int * foo;
public:
Foo(size_t size) : size(size), foo(new int[size]) {}
~Foo(){delete[] foo;} // note operator delete[], not delete
Foo(Foo const& other) : size(other.size), foo(new int[other.size]) {
copy(other.foo, other.foo + size, foo);
}
Foo& swap(Foo& other) {
std::swap(foo, other.foo);
std::swap(size, other.size);
return *this;
}
Foo& operator=(Foo g) {
return swap(g);
}
int& operator[] (const int idx) {return foo[idx];}
};
Run Code Online (Sandbox Code Playgroud)
和
class Bar {
private:
Foo bar;
public:
Bar(Foo foo) : bar(foo) {};
~Bar(){};
Bar(Bar const& other) : bar(other.bar) { }
Bar& swap(Bar &other) { bar.swap(other.bar); return *this; }
Bar& operator=(Bar other) { return swap(other); }
}
Run Code Online (Sandbox Code Playgroud)
整个使用相同的成语
正如对问题的评论中提到的那样,Bar自定义拷贝构造函数等是不必要的,但我们也假设Bar还有其他东西:-)
swap需要通过引用传递,因为两个实例都已更改.
需要通过引用传递复制构造函数,因为如果按值传递,则需要调用复制构造函数
是
不,但它并不总是最有效的做事方式
1 - 上面为Bar类实现的方法和构造函数是否安全?使用Foo的复制和交换后,我确保在分配或复制Bar时不会造成任何伤害?
关于复制者:这总是安全的(全有或全无).它要么完成(全部),要么抛出异常(没有).
如果您的类只由一个成员组成(即没有基类),赋值运算符将与成员类的安全性一样安全.如果您有多个成员,则赋值运算符将不再是"全有或全无".第二个成员的赋值运算符可以抛出异常,在这种情况下,对象将被分配为"中途".这意味着您需要在新类中再次实现复制和交换以获得"全部或全部"分配.
但是,在您不会泄漏任何资源的意义上,它仍然是"安全的".当然,每个成员的状态将是一致的 - 只是新类的状态不一致,因为一个成员被分配而另一个成员没有被分配.
2 - 在复制构造函数和swap中通过引用传递参数是必需的吗?
是的,通过引用传递是强制性的.复制构造函数是复制对象的构造函数,因此它不能通过值获取它的参数,因为这意味着必须复制参数.这将导致无限递归.(拷贝监视器将被调用参数,这意味着要为参数调用copy-ctor,这意味着......).对于swap,原因是另一个:如果你要按值传递参数,你永远不能使用swap来真正交换两个对象的内容 - 交换的"目标"将是最初传入的对象的副本,哪个会被立即销毁.
3 - 是否正确地说当operator =的参数通过值传递时,为此参数调用复制构造函数以生成对象的临时副本,并且该副本随后与*this交换?如果我在operator =中通过引用传递我会有一个大问题,对吗?
恩,那就对了.然而,通过引用-const获取参数,构造本地副本然后与本地副本交换也是很常见的.然而,by-to-const方式有一些缺点(它禁用了一些优化).如果你没有实现copy-and-swap,你应该通过reference-to-const传递.
4 - 在复制和分配Foo时,是否存在这种成语无法提供完全安全性的情况?
我不知道.当然,总是可以通过多线程(如果没有正确同步)使事情失败,但这应该是显而易见的.