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

StackOverflow https://stackoverflow.com/questions/210616

  •  03-07-2019
  •  | 
  •  

Вопрос

Я читаю этот 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;
  }

Насколько я могу "вывести", в первой строке указано только имя конструктора, для меня "::" звучит как "принадлежит".А код между {} - это само тело конструктора.

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

Прав Ли я в своих предположениях?"::" - это как принадлежит, а список после параметров и body - это как "аргументы по умолчанию" или что-то в этом роде?

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

Это было полезно?

Решение

Наиболее распространенным случаем является следующий:

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

Другие советы

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

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

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

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

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

{
   int bla;
   int bla;
}

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

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

ДА, ::является оператором области видимости 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;
};
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top