安全分配和复制交换习语

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)

现在我有一系列问题:

  1. 上面为Bar类实现的方法和构造函数是否安全?使用复制和交换后,Foo我确保在分配或复制时不会造成任何伤害Bar

  2. 在复制构造函数和swap中通过引用传递参数是必需的吗?

  3. 是否正确地说当operator=通过值传递参数时,为此参数调用复制构造函数以生成对象的临时副本,然后将此副本与之交换*this?如果我通过参考通过operator=我会有一个大问题,对吗?

  4. 是否存在这种习惯用法无法在复制和分配时提供完全安全的情况Foo

rlc*_*rlc 8

您应该尽可能在初始化列表中初始化类的成员.这也将照顾我在评论中告诉你的错误.考虑到这一点,您的代码变为:

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需要通过引用传递,因为两个实例都已更改.

需要通过引用传递复制构造函数,因为如果按值传递,则需要调用复制构造函数

第三个问题

第四个问题

不,但它并不总是最有效的做事方式


Pau*_*oke 5

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时,是否存在这种成语无法提供完全安全性的情况?

我不知道.当然,总是可以通过多线程(如果没有正确同步)使事情失败,但这应该是显而易见的.