Списки инициализаторов: конструкторы копирования и операторы присваивания = избыточность? - PullRequest
2 голосов
/ 09 ноября 2011

Кажется, что списки инициализаторов - хорошая идея для ваших конструкторов классов и, я полагаю, также для конструктора копирования. Для оператора присваивания необходимо назначить каждого члена в теле функции. Рассмотрим следующий простой блок:

class Foo {
private: 
  int a,b;
public:
  Foo(int c, int d)  : a(c), b(d) {}
  Foo(const Foo & X) : a(X.a), b(X.b) {}
  Foo& operator=(const Foo& X) {
    if (this == &X) return *this;
    a = X.a;
    b = X.b;
    return *this;
  }
};

Если класс имеет умеренное количество членов данных, есть три места, где можно испортить различные назначения / инициализацию. Под этим я подразумеваю, что если конструктор копирования будет выглядеть так:

  Foo(const Foo & X) : a(X.a), b(X.a) {}

или отсутствует строка оператора =. Поскольку оператор присваивания и конструктор копирования часто имеют одинаковый эффект (в том смысле, что мы копируем элементы из одного Foo в другой), могу ли я «повторно использовать» код из конструктора копирования или оператора присваивания, или наоборот?

Ответы [ 3 ]

10 голосов
/ 09 ноября 2011

Ваша цель должна состоять в том, чтобы вообще не писать конструкторы / операторы копирования. Ваша цель должна состоять в том, чтобы позволить компилятору сделать это. Контейнеры стандартной библиотеки можно копировать, поэтому используйте их там, где это целесообразно.

Если есть элементы, которые нельзя скопировать правильно, используйте интеллектуальные указатели или другие объекты RAII. Это те объекты, которые должны нуждаться в специальных конструкторах / назначениях копирования. И они нужны им только для одного члена.

Все остальное не должно их использовать.

3 голосов
/ 09 ноября 2011

Поскольку оператор присваивания и конструктор копирования часто имеют одинаковый эффект.

Совсем нет, один выполняет инициализацию, а другой выполняет присваивание.Они различаются по исходному состоянию объекта, и их задачи различны (хотя и схожи).Оператор канонического присваивания обычно выполняется следующим образом:

Foo& operator=(Foo right) {
    right.swap( *this );
    return *this;
}
2 голосов
/ 09 ноября 2011

Возможно, недопустимо перенаправлять все в оператор присваивания, но это было обычным в C ++ 03, где это было разрешено.

В C ++ 11 конструкторы проще: перенаправьте все конструкторы в один главный конструктор.

class Foo {
private: 
  int a,b;
public:
  Foo(int c, int d)  : a(c), b(d) {}
  Foo(const Foo & X) : Foo(x.a, x.d) {} 
  //syntax may be wrong, I don't have a C++11 compiler
  Foo& operator=(const Foo& X) {
    if (this == &X) return *this;
    a = X.a;
    b = X.b;
    return *this;
  }
}

В C ++ 03 (где это разрешено)

class Foo {
private: 
  int a,b;
  void init(int c, int d) {a=c; b=d;}
public:
  Foo(int c, int d)  : {init(c,d);}
  Foo(const Foo & X) : {init(X.a, X.b);} 
  Foo& operator=(const Foo& X) { init(X.a, X.b);} 
}

Но имейте в виду, что это не может использоваться в некоторых распространенных случаях. (любой объект, который не может быть назначен)

...