Двоеточие в определении конструктора

234
18 января 2018, 21:07

У меня возникла проблема с пониманием синтаксиса. Увидел вот такой конструктор:

foo(char *msg) : msg(msg) { ..... }

Что значит двоеточие после аргумента в первых скобках?

Answer 1

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

Используется как для инициализации членов класса, так и для вызова конструктора базового/базовых классов, т.е. по сути, инициализации базовой составляющей. Так же здесь может быть вызов другого конструктора текущего класса (делегирование конструкторов, начиная с c++11).

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

struct S {
   S(int) {}  // Конструктор с параметром. Не является конструктором по умолчанию.
};
class B {
public:
   B(int i) : i(i), r(i), s(i) {
    // this->i = i; // Ошибка. Нельзя присваивать константе.
    // r = i;       // Ошибка. Не является инициализацией ссылки.
    // s = S(i);    // Ошибка. `s` должен быть создан в инициализаторе конструктора.
   }
private:
   const int i;
   int& r;
   S s; 
};

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

Дополнительно стоит заметить (как было упомянуто в комментарии Monah Tuk), что если член-класса не инициализирован явно в инициализаторе конструктора и при этом ему присваивается значение в теле конструктора, то он сначала будет инициализирован конструктором по умолчанию (или инициализацией в определении класса (c++11)):

class D {
    int i = 42; // Инициализация члена в определении класса
};

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

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

Answer 2
class foo
{
public:
   foo(int a) : a(a) {}
   // эквивалентно foo(int a) { this->a = a; }
private:
   int a;
}

Это вообще-то база синтаксиса определения конструкторов в плюсах.

Еще может быть:

class foo : public bar 
{
public:
   foo(int a) : bar(a) { ... }
}

В этом случае, после двоеточия вызывается конструктор базового класса. ЕМНИП (могу ошибаться), в случае базового класса это единственный верный способ его вызова, т.е. написать вот так:

foo(int a) { bar(a); ... }

будет неверно.

П.С. Думаю, ответ alexolut более полон, чем мой.

Answer 3

Это использование списка инициализации (Initialization List) для конструктора. Эта запись эквивалентна строчке кода в начале конструктора

this->msg = msg;
READ ALSO
Проблема с DLL библиотеками QT

Проблема с DLL библиотеками QT

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

203
Ошибка при выделении строки QTableWidget

Ошибка при выделении строки QTableWidget

Нужно добавить строку и программа падает при попытке выделить ее, то есть тут // Выделяем добавленную запись; uitable->setCurrentCell(insertPos,...

225
C++ перегрузка оператора “+” и метод push

C++ перегрузка оператора “+” и метод push

Всем приветЯ изучаю классы и перегрузки операций в С++

222
Отслеживание нажатия клавиш на Mac

Отслеживание нажатия клавиш на Mac

Мне нужно отследить нажатие клавиши F4 на OSX и выполнить действие, пишу на С++, желательно код обьяснить и без зависимостей

224