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

Я читаю этот открытый код на С++, и я пришел к конструктору, но я его не понимаю (в основном потому, что я не знаю С++: 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;
  }

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

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

Я прав в своих предположениях? "::" как принадлежит, и список после параметров и тела подобен "аргументам по умолчанию" или чему-то?

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

Ответ 1

Наиболее распространенный случай:

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. Во-вторых, вы правы в предположении, что тело конструктора встречается в фигурных скобках.

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

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

Ответ 2

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

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

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

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

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

{
   int bla;
   int bla;
}

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

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
{}

Ответ 8

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

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

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

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

В приведенном ниже примере показано, что намерение состоит в инициализации 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;
};