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


недавно я видел такой пример:

#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++?

12 272

12 ответов:

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

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

точка выноса из записи FAQ заключается в том, что

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

Foo(int num): bar(num)    

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

проще говоря, это инициализирует ваш значение 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 не имеет конструктора по умолчанию, поэтому для инициализации его члена нужно будет использовать список инициализаторов членов.

важные моменты при использовании члена Списки Инициализаторов:

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

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

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

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

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

// 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

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

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

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

в основном, в вашем случае,x устанавливается с _x,y С _y,z С _z.

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

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

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

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

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

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

vs

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

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

string();

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

string& operator=( const string& s );

будет вызван на 'str', как вы делаете str = p;

как во втором примере, ул. будут инициализированы сразу мимо вызов его конструктора

string( const string& s );

С 'p' в качестве аргумента.

вы правы, это действительно способ инициализации переменных-членов. Я не уверен, что в этом есть много пользы, кроме четкого выражения того, что это инициализация. Наличие "bar=num" внутри кода может быть перемещено, удалено или неверно истолковано гораздо легче.

есть еще одно "благо"

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

Это список инициализации конструктора. Вместо построения по умолчанию x,y и z и затем присваивая им значения, полученные в параметрах, эти члены будут инициализированы с этими значениями сразу же. Это может показаться не очень полезным для floats, но это может быть довольно экономия времени с пользовательскими классами, которые дорого построить.

еще не упоминалось в этом потоке: начиная с C++11, список инициализаторов членов может использовать инициализацию списка (aka. "инициализации", "приготовился инициализации"):

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

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