Для чего хорош список переменных-членов после двоеточия в конструкторе?

Я читаю этот открытый исходный код C++, и я пришел к конструктору, но я не понимаю его (в основном потому, что я не знаю C++ :P )

Я очень хорошо понимаю, C и Java.

 TransparentObject::TransparentObject( int w, int x, int y, int z ) : 
     _someMethod( 0 ),
     _someOtherMethod( 0 ),
     _someOtherOtherMethod( 0 ),
     _someMethodX( 0 ) 
  {
       int bla;
       int bla;
  }

насколько я могу "вывести", первая строка объявляет только имя construtor,":: "звучит как" принадлежит " мне. И код между {} - Это тело конструктора, которое он сам.

Я "думаю", что после пареметров и первых " {"похожи на методы по умолчанию параметры или что-то еще, но я не нахожу разумного объяснения в интернете. Большинство конструкторов C++, которые я нашел в примерах, почти идентичны конструкторам Java.

Я прав в своих предположениях? ":: "похоже, принадлежит, а список после параметров и тела похож на" default args " или что-то еще?

обновление: Спасибо за ответы. Может быть эти методы? (Я думаю, нет ) и в чем разница вызова их в конструкторе тело

8 ответов


самый распространенный случай таков:

class foo{
private:
    int x;
    int y;
public:
    foo(int _x, int _y) : x(_x), y(_y) {}
}

это x и y к значениям, которые приведены в _x и _y в параметрах конструктора. Это часто лучший способ построить любые объекты, объявленные как члены данных.

также возможно, что вы смотрели на цепочку конструктора:

class foo : public bar{
    foo(int x, int y) : bar(x, y) {}
};

в этом случае конструктор класса вызовет конструктор своего базового класса и передаст значения x и y.

чтобы еще больше рассечь функцию:

TransparentObject::TransparentObject( int w, int x, int y, int z ) : 
   _someMethod( 0 ),
   _someOtherMethod( 0 ),
   _someOtherOtherMethod( 0 ),
   _someMethodX( 0 ) 
{
     int bla;
     int bla;
}

на ::-оператор называется оператором разрешения области. Это просто указывает на то, что TransparentObject является членом TransparentObject. Во-вторых, вы правы, предполагая, что тело конструктора происходит в фигурных скобках.

UPDATE: Спасибо за ответы. Может быть эти методы? (Я думаю, нет ) и какая разница называть их в теле конструктора

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


вы очень близки. Первая строка-декларация. Метка слева от :: имя класса и конструктора, имя функции должно совпадать с именем класса.

TransparentObject::TransparentObject( int w, int x, int y, int z )

В C++ можно поставить двоеточие и некоторые начальные значения для переменных-членов до начала тела функции. Этот метод должен использоваться, если вы не initialzing любой const переменные или передача параметров в суперкласс конструктор.

: 
 _someMethod( 0 ),
 _someOtherMethod( 0 ),
 _someOtherOtherMethod( 0 ),
 _someMethodX( 0 )

а затем появляется тело конструктора в фигурных скобках.

{
   int bla;
   int bla;
}

:: на самом деле означает содержит (см. комментарии для уточнения), однако _someMethods и так далее-это то, что называется список инициализации. Существует много информации по ссылке =]

EDIT: извините, мое первое предложение неверно-см. комментарии.


Yes,:: является оператором области C++, который позволяет сообщить компилятору, к чему принадлежит функция. Использование: после объявления конструктора запускается то, что называется списком инициализации.


код между списком аргументов и {}s указывает инициализацию (некоторых) членов класса.

инициализация в отличие от назначения - - - это разные вещи - - - так что все это вызовы конструкторов.


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


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

class A
{
public:
  A();
private:
  B _b;
  C& _c;
};

A::A( C& someC )
{
  _c = someC; // this is illegal and won't compile. _c has to be initialized before we get inside the braces
  _b = B(NULL, 5, "hello"); // this is not illegal, but B might not have a default constructor or could have a very 
                            // expensive construction that shouldn't be done more than once
}

в этой версии:

A::A( C& someC )
: _b(NULL, 5, "hello") // ok, initializing _b by passing these arguments to its constructor
, _c( someC ) // this reference to some instance of C is correctly initialized now
{}

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

члены класса "инициализированы" в теле конструктора (т. е. между фигурными скобками {} с использованием оператора=) не являются технически инициализацией, это назначение. Для классов с нетривиальным конструктором / деструктором может быть дорогостоящим построение по умолчанию, а затем изменение через назначение таким образом. Для справки членов вы должны используйте список инициализаторов, так как они не могут быть изменены с помощью оператора присваивания.

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

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

в надуманном примере ниже похоже, что намерение состоит в инициализации m_b С value затем m_a С m_b, но на самом деле происходит то, что m_a начинаются с m_b (который сам еще не инициализирован) тогда m_b инициализируется с помощью value. m_b будет просто содержать мусор!

struct BadInitialiserListExample
{
    BadInitialiserListExample(int value) :
        m_b(value),
        m_a(m_b)      // <-- *BUG* this is actually executed first due to ordering below!
    {
    }

    int    m_a;
    int    m_b;
};