Инициализация в конструкторах, лучшая практика? - PullRequest
6 голосов
/ 31 марта 2009

Я некоторое время программировал на C ++ и использовал оба метода:

class Stuff {
public:
     Stuff( int nr ) : n( nr ) { }
private:
     int n;
}

Или

class Stuff {
public:
     Stuff( int nr ) { 
         n = nr;
     }
private:
     int n;
}

Примечание: это не то же самое, что , это , похоже, но не то же самое.

Что считается лучшей практикой?

Ответы [ 7 ]

21 голосов
/ 31 марта 2009

Списки инициализаторов являются предпочтительными. См. FAQ 10.6

12 голосов
/ 31 марта 2009

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

Когда вы используете тело конструктора для инициализации объекта, вы должны правильно обрабатывать исключения и разматывать объект соответствующим образом. Это обычно намного сложнее понять.

5 голосов
/ 31 марта 2009

Используйте список инициализаторов, когда это возможно. Для int это не имеет большого значения в любом случае, но для более сложного объекта-члена вы в конечном итоге получите конструктор по умолчанию вызываемого объекта с последующим присваиванием этому объекту, которое может закончиться медленнее.

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

2 голосов
/ 31 марта 2009

Если возможно, используйте первую версию.

Первый - инициализация с использованием списков инициализаторов и фактически вызывает конструкторы членов.

Второе назначение. Если бы n имел тип с конструктором по умолчанию, он уже был бы вызван, и тогда вы бы присвоили его. Если n не имеет конструктора по умолчанию, вы будете вынуждены использовать первый тип. Аналогично, если n было ссылкой: int &n.

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

1 голос
/ 31 марта 2009

Я обычно пытаюсь сделать список инициализаторов, когда могу. С одной стороны, это делает явным то, что вы инициализируете код в конструкторе. const memebers имеют , которые должны быть инициализированы таким образом.

Если вы просто поместите код в тело конструктора, вполне возможно, что кто-то решит прийти и перенести большую его часть в подпрограмму «не-конструктор» позже.

Это может быть принято за борт, хотя. У меня есть коллега, который любит создавать классы, которые имеют 2 страницы кода инициатора, без кода конструктора и, возможно, 2 страницы для всего остального кода класса. Мне действительно трудно это читать.

0 голосов
/ 31 марта 2009

Хочу добавить, что вам не нужно объявлять список инициализаторов в заголовке (.h). Это можно сделать при реализации конструктора (что очень распространено).

Итак:

//Stuff.h
class Stuff {
public:
     Stuff( int nr );
private:
     int n;
}

//Stuff.cpp
Stuff::Stuff(int nr)
: n(nr)
{
    //initalize complex members
}

законно и imo концентрирует инициализацию полей там, где это важно. Иногда нам нужно инициализировать сложные элементы в теле, поэтому у вас есть список инициализаторов и комплексная инициализация в файле .cpp.

0 голосов
/ 31 марта 2009

Второй вариант - не инициализация, а присвоение. С типами, которые имеют определяемые пользователем конструкторы по умолчанию, вторая опция будет вызывать конструктор по умолчанию, а затем вызывать оператор присваивания (независимо от того, определен он пользователем или нет) для присвоения значения.

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

Массивы могут быть инициализированы значением в списке инициализации, но не в теле конструктора:

class X {
public:
   X() : array() {} // value-initializes the array
// equivalent to:
// X() { for ( int i = 0; i < 10; ++i ) array[i]=0; }    
private:
   int array[10];
};

Для типов POD их можно инициализировать по значению в списке инициализации, но не в скобках:

class X {
public:
   X() : pod() {} // value-initializes
// equivalent to (but easier to read and subtly faster as it avoids the copy):
// X() { pod = {}; } 
private:
   PODType pod;
};

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

class X
{
public:
   X() : v(10) {} // construct a vector of 10 default initialized integers
// equivalent to:
// X() { for ( int i = 0; i < 10; ++i ) v.push_back(0); }
private:
   std::vector<int> v;
};

Наконец, всякий раз, когда они фактически эквивалентны, списки инициализации более идиоматичны в C ++.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...