Была ли новая функция инициализации члена C++11 при объявлении сделала списки инициализации устаревшими?
С C++11 у нас теперь есть возможность инициализировать членов класса в объявлении заголовка:
class aClass
{
private:
int mInt{100};
public:
aClass();
~aClass();
};
Так что я немного растерялся. Традиционно списки инициализации в конструкторах использовались для инициализации члена:
aClass::aClass()
: mInt(100)
{
...
}
Была ли новая функция инициализации члена C++11 при объявлении сделала списки инициализации устаревшими? Если нет, каковы преимущества одного над другим? Какие ситуации могут сделать инициализацию при объявлении выгодной или списки инициализации выгодными? Когда один должен быть использован поверх другого?
3 ответа
Нет, они не устарели, как говорится в этой статье. Познакомьтесь с новыми формами инициализации C++11 в разделе " Инициализация членов класса " (выделено мое):
Имейте в виду, что если один и тот же элемент данных имеет в конструкторе как инициализатор члена класса, так и mem-init, последний имеет приоритет. Фактически, вы можете воспользоваться этим поведением, указав значение по умолчанию для члена в форме инициализатора члена класса, который будет использоваться, если конструктор не имеет явного mem-init для этого члена. Иначе, mem-init конструктора вступит в силу, переопределив инициализатор члена класса. Этот метод полезен в классах, которые имеют несколько конструкторов
Таким образом, хотя инициализация членов класса является хорошим удобством, она не устраняет необходимость в списках инициализации, но обе функции вместо этого работают вместе, чтобы дать вам хороший способ указать значения по умолчанию и переопределить их при необходимости. Похоже, именно так Бьярне Страуструп и видит это, он говорит:
Это немного экономит время при наборе текста, но реальные преимущества приходят в классах с несколькими конструкторами. Часто все конструкторы используют общий инициализатор для члена:
и предоставляет пример членов, которые имеют общий инициализатор:
class A {
public:
A(): a(7), b(5), hash_algorithm("MD5"), s("Constructor run") {}
A(int a_val) : a(a_val), b(5), hash_algorithm("MD5"), s("Constructor run") {}
A(D d) : a(7), b(g(d)), hash_algorithm("MD5"), s("Constructor run") {}
int a, b;
private:
HashingFunction hash_algorithm; // Cryptographic hash to be applied to all A instances
std::string s; // String indicating state in object lifecycle
};
и говорит:
Тот факт, что hash_algorithm и s каждый имеет одно значение по умолчанию, теряется в коде и может легко стать проблемой во время обслуживания. Вместо этого мы можем выделить инициализацию элементов данных:
class A {
public:
A(): a(7), b(5) {}
A(int a_val) : a(a_val), b(5) {}
A(D d) : a(7), b(g(d)) {}
int a, b;
private:
HashingFunction hash_algorithm{"MD5"}; // Cryptographic hash to be applied to all A instances
std::string s{"Constructor run"}; // String indicating state in object lifecycle
};
Примечание: недостаток в C++11
Есть один недостаток использования при инициализации членов класса в C++11, так как он делает класс неагрегированным, мы больше не можем использовать агрегатную инициализацию, что может быть довольно неожиданным. Это не тот случай в C++14, где это ограничение было снято. См. C++11 агрегатная инициализация для классов с нестатическими инициализаторами членов для получения дополнительной информации.
Нет, они не устарели.
Списки инициализации по-прежнему являются единственным способом, если вам нужны аргументы конструкторов для инициализации членов вашего класса.
class A
{
int a=7; //fine, give a default value
public:
A();
};
class B
{
int b;
public:
B(int arg) : b(arg) {}
B(int arg, bool b) : b(arg) { ... }
};
Обратите внимание, что если присутствуют оба, инициатор конструктора вступит в силу, переопределив инициализацию члена класса, что полезно для указания значения по умолчанию для члена класса.
На мой взгляд, инициализация в классе - это усовершенствование списков mem-initializer-lists. В C++03 члены, не перечисленные в списке mem-initializer-list, всегда инициализируются по умолчанию. Это означает конструктор по умолчанию для классов и отсутствие инициализации для примитивных типов.
Инициализация в классе просто позволяет вам указать ваши собственные значения по умолчанию. Есть два способа посмотреть на это.
Первый: если большинство / все конструкторы вашего класса хотят предоставить одно и то же начальное значение для члена, используйте для этого члена инициализатор в классе. Для других участников используйте mem-initializer-lists. Вам, конечно, придется использовать их всякий раз, когда начальное значение зависит от аргументов конструктора.
Другой: предоставьте в своем классе инициализатор для всех членов, именно так, как конструктор по умолчанию вашего класса будет их инициализировать. Затем mem-initializer-lists в конструкторах, отличных от заданных по умолчанию, получают семантику "чем он отличается от объекта, созданного по умолчанию".