Что значит C() : a(12) {}
Недавно сейчас ООП в с++ и недавно наткнулся на вот такой интересный код в интернете:
struct A1
{
A1()
{cout << "A1";}
~A1
{ cout << "~A1";}
};
struct A2
{
A2()
{cout << "A2";}
~A2()
{ cout << "~A2";}
};
class B
{
public:
B()
{ cout << "B";}
~B()
{ cout << "~B";}
private:
A1 a;
};
class C : public B
{
public:
C() : a(12)
{ cout << "C"; }
~C()
{ cout << "~C"; }
private:
A2 a;
};
int main()
{ C c; }
Можете пожалуйста мне объяснить, что такое
C() : a(12) {}
Ответы (3 шт):
Конструкция вида
C() : a(12)
{ cout << "C"; }
это такой способ задать значение переменной класс a до вызова конструктора. Это способ считается достаточно хорошим и оптимальным в плане оптимизации. В принципе, он практически эквивалентный следующему коду
C()
{a = 12; cout << "C"; }
внимание в текущем варианте кода а - это A2, у которого нет подходящего конструктора с одним int, Но есть подозрение, что автор неточно скопировал свой код и там было что то подходящее - например, там был тип int (или это был вопрос на тему "исправь, что бы компилировалось)
Но если Вы хотите сделать так как Вы написали изначально, то это можно (это же с++!)
// можно использовать макрос, можно constexpr функцию
//#define b
constexpr int b(int x) { return x;}
struct Test
{
// а это битовое поле:)
int a:b(2) {};
};
Родственный вопрос - Порядок вызова в списке инициализации конструктора
В определении конструктора класса после двоеточия обязательно нужно вызывать конструкторы базового класса, и элементов этого класса в правильном порядке.
Сначала базовые по-порядку, затем переменные по-порядку. Если базовые классы или элементы имеют конструкторы по-умолчанию, то их можно не указывать, они (конструкторы) будут вызваны без аргументов.
Ссылки например, не имеют коструктора по-умолчанию. И им нужен аргумент при создании объекта. Константные переменные тоже, конструируются только один раз и только в этом списке конструкции.
class BaseLeft {
public :
// конструктора по-умолчанию нет
// у типа int есть, но он не вызовется автоматически
BaseLeft(int x) /* : i { } */ { i = x; }
private :
int i ;
} ;
class BaseRight {
public :
// конструктор по-умолчанию есть
// у типа int есть, но его можно вызвать
BaseRight() : i { -1 } { }
private :
int i ;
} ;
class Son : public BaseLeft , public BaseRight {
public :
// Son(int & j) : BaseLeft{} , BaseRight{} , bl{} , br{} , i{} ,l{} { c = 7 ; }
// error: no matching function for call to
// ‘BaseLeft::BaseLeft(<brace-enclosed initializer list>)’
// В классе BaseLeft нет конструктора по-умолчанию
// error: assignment of read-only member ‘Son::c’
// присваивание константе нельзя, только при инициализации
// error: cannot bind non-const lvalue reference of type ‘int&’
// to an rvalue of type ‘int’
// у ссылки нет конструктора по-умолчанию, ей нужен адрес куда ссылаться
//Son(int & j) : BaseLeft{1} , BaseRight{} , bl{1} , br{} , i{}, l{j} , c{j} { }
// хорошо
Son(int & j) : BaseLeft{1} , bl{1} , l{j} , c{j} { i = 0 ; }
// то-же самое
private :
BaseLeft bl ;
BaseRight br ;
int i ;
int & l ;
int const c ;
};
Примечание :
Для встроенных типов (вроде int) стандарт решил, что если не вызывать прямо конструктор по-умолчанию, то можно вообще не инициализировать эту переменную. И в памяти это будет переменная со значением мусора в памяти.
Это прямая инициализация. Именно такой способ рекомендуется! Списки инициализации членов позволяют инициализировать члены, а не присваивать им значения. Это единственный способ инициализации констант и ссылок, которые являются переменными-членами вашего класса. Во многих случаях использование списка инициализации может быть более результативным, чем присваивание значений переменным-членам в теле конструктора. Списки инициализации работают как с переменными фундаментальных типов данных, так и с членами, которые сами являются классами.