Подтвердить что ты не робот

С++ Инициализация полей непосредственно к списку инициализации в конструкторе по умолчанию

Я хотел бы знать, есть ли разница между этим кодом:

class Foo{
 private:
    int a = 0;
 public:
    Foo(){}
}

и

class Foo{
 private:
    int a;
 public:
    Foo(): a(0) {}
}

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

Кроме того, что касается нижеприведенного случая:

class Foo{
 private:
   int a = 0;
 public:
   Foo(){}
   Foo(int i): a(i) {}
}

Когда вызывается нестандартный конструктор: "а" инициализируется дважды, сначала до 0, затем до "i" или непосредственно к "i"?

4b9b3361

Ответ 1

От cppreference - нестатические элементы данных

Инициализация члена
1) В списке инициализатора члена конструктора.
2) Через инициализатор элемента по умолчанию, который является просто скобкой или равен инициализатору, включенному в декларацию члена, который используется, если член опущен в списке инициализаторов членов.

Если член имеет инициализатор элемента по умолчанию, а также появляется в списке инициализации члена в конструкторе, инициализатор элемента по умолчанию игнорируется.


В заключение, оба инициализатора эквивалентны и выполняют то, что они должны делать.

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

class Foo {
private:
    int a = 0;
};

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

class Foo {
private:
    int a;
public:
    Foo() : a(3) {}
    Foo(int i) : a(i) {}
};

Ответ 2

Первый набор примеров идентичен друг другу.

В последнем примере стандарт С++ указывает следующее:

12.6.2 Инициализация баз и членов

[...]

Если данный нестатический член данных имеет как скользящий или равный-инициализатор и mem-инициализатор, инициализация заданный mem-инициализатором, и нестатические данные Элемент-член-бит-или-равный-инициализатор игнорируется. [Пример: данный

struct A {
int i = /∗ some integer expression with side effects ∗/ ;
A(int arg) : i(arg) { }
// ...
};

конструктор A (int) просто инициализирует я значением arg, и побочные эффекты в состоянии, равные-равному-инициализатору, не будут место. - конец примера]

Ответ 3

Оба идентичны.

Одно из правил разработки программного обеспечения DRY - не повторяйте себя. DRY указывает, что если вы можете не повторять один и тот же токен дважды или иметь два идентичных списка, вы должны.

Это по нескольким причинам. Поддержание двух идентичных списков удивительно подвержено ошибкам; один изменен или имеет опечатку, а другой нет. Это делает код длиннее, что затрудняет его чтение. И, избегая кодирования вставки, поощряется использование некоторых очень мощных и выразительных методов, которые могут сделать то, что вы делаете яснее, чем делать это вручную 17 раз.

struct foo {
  int a;
  foo():a(7) {}
};

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

struct foo {
  int a = 7;
  foo() {}
};

Здесь мы не повторяемся.

struct foo {
  int a = 7;
  foo() {}
  foo(int i):a(i) {}
};

Здесь есть некоторое повторение, но повторение неизбежно. Это, однако, сведено к минимуму.

Здесь есть некоторая стоимость, потому что кто-то может интерпретировать a=7 как "он всегда начинается с 7", а не "по умолчанию - 7".

struct foo {
  int a = 7;
  foo():a(3) {}
  foo(int i):a(i) {}
};

И выше это ужасный анти-шаблон.