Подтвердить что ты не робот

Инициализация полей в конструкторе - список инициализаторов и тело конструктора

Я работаю на С++ в течение некоторого времени, но я не уверен в различии между

public : Thing(int _foo, int _bar): member1(_foo), member2(_bar){}

и

public : Thing(int _foo, int _bar){
    member1 = _foo;
    member2 = _bar;
}

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

Не совсем привык к первому примеру, поэтому, если я допустил ошибку, прошу прощения.

4b9b3361

Ответ 1

Они не совпадают, если member1 и member2 не являются POD (т.е. не P lain O ld D ata):

public : Thing(int _foo, int _bar){
    member1 = _foo;
    member2 = _bar;
}

эквивалентно

public : Thing(int _foo, int _bar) : member1(), member2(){
    member1 = _foo;
    member2 = _bar;
}

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

Ответ 2

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

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

Update

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

Ответ 3

Единственное, что нужно добавить в ответ Péter Török, - это то, что список инициализаторов является единственным способом инициализации элементов-членов const, то есть:

class foo
{
public:

    foo(int value)
        : myConstValue(value)
    {};

    foo()
    {
        myConstValue = 0; // <=== Error! myConstValue is const (RValue), you can't assign!
    };

private:
    const int myConstValue;
}

Ответ 4

В вашем примере кода первый в инициализации конструктора, а второй - присваивание внутри тела конструктора.

Список инициализации конструктора - лучший способ выполнить всю инициализацию члена, поскольку он повышает производительность.

class A
{
string name;
public:
A(string myname):name(myname) {}
}

В приведенном выше случае компилятор не создает временный объект для инициализации. Однако в следующем случае:

A::A()
{
    name = myname;
}

Создается отдельный временный объект, и этот временный объект передается оператору присваивания string для назначения name. Затем временный объект уничтожается, что не очень эффективно.

Примечание. Обязательно, чтобы ссылочный или константный член должен быть инициализирован в списке инициализации конструктора. Они не могут быть "назначены" в теле конструктора.

Ответ 5

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

Ответ 6

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

class Demo
{
    int a;
    int b;
public:
    Demo(int a,int b):a(a),b(b)
    {

    }

};

если мы инициализируем a и b внутри конструктора, это будет самоопределение.