Для чего нужен список переменных-членов после двоеточия в конструкторе? - PullRequest
11 голосов
/ 17 октября 2008

Я читаю этот открытый исходный код 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;
  }

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

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

Я прав в своих предположениях? «::» как принадлежит, а список после params и body похож на «args по умолчанию» или что-то в этом роде *

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

Ответы [ 8 ]

19 голосов
/ 17 октября 2008

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

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

10 голосов
/ 17 октября 2008

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

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

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

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

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

{
   int bla;
   int bla;
}
7 голосов
/ 17 октября 2008

:: На самом деле означает, что содержит (см. Комментарии для пояснения), однако _someMethods и так далее, это то, что называется список инициализации По ссылке достаточно информации =]

РЕДАКТИРОВАТЬ: Извините, мое первое предложение неверно - см. Комментарии.

2 голосов
/ 17 октября 2008

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

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

2 голосов
/ 17 октября 2008

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

1 голос
/ 30 ноября 2008

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

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
{}
1 голос
/ 17 октября 2008

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

0 голосов
/ 12 ноября 2011

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

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

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

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

В нижеприведенном надуманном примере похоже, что намерение состоит в том, чтобы инициализировать 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;
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...