Списки инициализаторов: конструкторы копирования и операторы присваивания = избыточность?
Кажется, что списки инициализаторов - хорошая идея для ваших конструкторов классов, и, я полагаю, также для конструктора копирования. Для оператора присваивания необходимо назначить каждого члена в теле функции. Рассмотрим следующий простой блок:
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 ответа
Ваша цель должна состоять в том, чтобы вообще не писать конструкторы / операторы копирования. Ваша цель должна состоять в том, чтобы позволить компилятору сделать это. Контейнеры стандартной библиотеки можно копировать, поэтому используйте их там, где это целесообразно.
Если есть элементы, которые нельзя скопировать правильно, используйте интеллектуальные указатели или другие объекты RAII. Те объекты, которые должны нуждаться в специальных конструкторах / назначениях копирования. И они нужны им только для одного члена.
Все остальное не должно их использовать.
Поскольку оператор присваивания и конструктор копирования часто имеют одинаковый эффект.
Вовсе нет, один выполняет инициализацию, а другой выполняет присваивание. Они различаются по исходному состоянию объекта, и их задачи различны (хотя и схожи). Оператор канонического присваивания обычно выполняется как:
Foo& operator=(Foo right) {
right.swap( *this );
return *this;
}
Возможно, недопустимо перенаправлять все в оператор присваивания, но это было обычным в 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);}
}
Но имейте в виду, что это не может быть использовано в некоторых распространенных случаях. (любой объект, который не может быть назначен)