Что это за странный синтаксис двоеточия (& quot;: & quot;) в конструкторе?

Недавно я видел пример, подобный следующему:

#include <iostream>

class Foo {
public:
int bar;
Foo(int num): bar(num) {};
};

int main(void) {
std::cout << Foo(42).bar << std::endl;
return 0;
}

Что это странно : bar(num) имею в виду? Кажется, что-то инициализирует переменную-член, но я никогда раньше не видел этот синтаксис. Это похоже на вызов функции / конструктора, но для int? Не имеет смысла для меня. Возможно, кто-то мог бы просветить меня. И, кстати, есть ли какие-либо другие особенности эзотерического языка, подобные этой, которые вы никогда не найдете в обычной книге C ++?

282

Решение

Это список инициализации членов. Вы должны найти информацию об этом в любом хорошая книга C ++.

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

Вывод из часто задаваемых вопросов заключается в том, что

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

174

Другие решения

Foo(int num): bar(num)

Эта конструкция называется Список инициализаторов в C ++.

Проще говоря, это инициализирует ваш член bar к стоимости num,


В чем разница между инициализацией и назначением внутри конструктора?

Инициализация участника:

Foo(int num): bar(num) {};

Назначение участника:

Foo(int num)
{
bar = num;
}

Существует значительная разница между инициализацией элемента с использованием списка инициализатора элемента и присвоением ему значения внутри тела конструктора.

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

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

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

Cost of Member Initialization = Object Construction
Cost of Member Assignment = Object Construction + Assignment

Последнее на самом деле эквивалентно:

Foo(int num) : bar() {bar = num;}

В то время как первое эквивалентно просто:

Foo(int num): bar(num){}

Для встроенных (ваш пример кода) или членов класса POD практических затрат нет.


Когда вы должны использовать список инициализатора участника?

Ты сможешь иметь (скорее вынуждены) используйте список инициализатора участника, если:

  • В вашем классе есть референтный член
  • В вашем классе есть нестатический член const или
  • У вашего ученика нет конструктора по умолчанию или
  • Для инициализации членов базового класса или
  • Когда имя параметра конструктора совпадает с элементом данных (на самом деле это НЕ ДОЛЖНО)

Пример кода:

class MyClass
{
public:
//Reference member, has to be Initialized in Member Initializer List
int &i;
int b;
//Non static const member, must be Initialized in Member Initializer List
const int k;

//Constructor’s parameter name b is same as class data member
//Other way is to use this->b to refer to data member
MyClass(int a, int b, int c):i(a),b(b),k(c)
{
//Without Member Initializer
//this->b = b;
}
};

class MyClass2:public MyClass
{
public:
int p;
int q;
MyClass2(int x,int y,int z,int l,int m):MyClass(x,y,z),p(l),q(m)
{
}

};

int main()
{
int x = 10;
int y = 20;
int z = 30;
MyClass obj(x,y,z);

int l = 40;
int m = 50;
MyClass2 obj2(x,y,z,l,m);

return 0;
}
  • MyClass2 не имеет конструктора по умолчанию, поэтому его нужно инициализировать через список инициализаторов членов.
  • Базовый класс MyClass не имеет конструктора по умолчанию, поэтому для инициализации его члена нужно будет использовать Member Initializer List.

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

Переменные-члены класса всегда инициализируются в том порядке, в котором они объявлены в классе.

Они есть не инициализируется в том порядке, в котором они указаны в списке инициализаторов элементов.
Короче говоря, список инициализации члена не определяет порядок инициализации.

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

267

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

Рассмотрим эти два примера:

// Example 1
Foo(Bar b)
{
bar = b;
}

// Example 2
Foo(Bar b)
: bar(b)
{
}

В примере 1:

Bar bar();  // default constructor
bar = b;  // assignment

В примере 2:

Bar bar(b) // copy constructor

Все дело в эффективности.

14

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

11

Это не неизвестно, это Синтаксис списка инициализации C ++

В основном, в вашем случае, x будет инициализирован с _x, y с _y, z с _z,

9

Другой уже объяснил вам, что синтаксис, который вы наблюдаете, называется «список инициализатора конструктора». Этот синтаксис позволяет вам инициализировать пользовательские базовые подобъекты и дочерние подобъекты класса (вместо того, чтобы позволить им инициализировать по умолчанию или оставаться неинициализированными).

Я просто хочу отметить, что синтаксис, который, как вы сказали, «выглядит как вызов конструктора», не обязательно является вызовом конструктора. На языке C ++ () синтаксис является лишь одной стандартной формой синтаксис инициализации. Это интерпретируется по-разному для разных типов. Для типов классов с определяемым пользователем конструктором это означает одно (это действительно вызов конструктора), для типов классов без определяемого пользователем конструктора это означает другое (так называемый инициализация значения ) для пустых ()) и для не классовых типов это снова означает что-то другое (так как не классовые типы не имеют конструкторов).

В вашем случае элемент данных имеет тип int, int это не тип класса, поэтому он не имеет конструктора. Для типа int этот синтаксис означает просто «инициализировать bar со значением num«И это все. Это делается просто так, напрямую, без участия конструкторов, так как, еще раз, int это не тип класса, поэтому он не может иметь никаких конструкторов.

8

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

7

Это список инициализации. Он инициализирует члены до запуска тела конструктора.
Рассматривать

class Foo {
public:
string str;
Foo(string &p)
{
str = p;
};
};

против

class Foo {
public:
string str;
Foo(string &p): str(p) {};
};

В первом примере str будет инициализирован конструктором без аргументов

string();

перед телом конструктора Foo. Внутри конструктора foo

string& operator=( const string& s );

будет вызываться на ‘str’, как вы делаете str = p;

Когда во втором примере str будет инициализирован напрямую
вызывая его конструктор

string( const string& s );

с «р» в качестве аргумента.

6
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector