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

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

Рассмотрим следующие классы

class test1
{
    private:
        int a;
        int b;
    public:
        test1():a(0),b(0){}
};

class test2
{
    private:
        int a;
        int b;
    public:
        test2()
        {
            a=0;
            b=0;
        }
};

Теперь я знаю, что test1() constructor - это правильный способ инициализировать элементы данных class, потому что в test2() мы выполняем назначение, а не инициализацию. Мои вопросы:

  • Что может пойти не так, если мы выполняем назначение вместо инициализации?
  • Разве компилятор не выполняет внутреннее выполнение в случае конструктора test1()? Если нет, то как они инициализируются?
4b9b3361

Ответ 1

Что может пойти не так, если мы выполняем назначение вместо инициализации?

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

Не выполняет ли компилятор внутреннее выполнение задания в случае конструктора test1()? Если нет, то как они инициализируются?

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

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

Ответ 2

Для вашего примера нет реальных отличий, потому что вы инициализируете простые целые числа.

Но предположим, что эти целые числа являются объектами с конструкторами, тогда компилятор будет генерировать следующие вызовы:

// test1
a::copy_constructor(0);
b::copy_constructor(0);


// test2
a::default_constructor();
b::default_constructor();
a::operator = (0);
b::operator = (0);

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

Ответ 3

Третий вариант, использующий инициализацию прямого списка. Преимущества

  1. переменные не должны быть инициализированы по умолчанию в конструкторе
  2. конструктор, а не оператор присваивания copy/move используется для построения членов (как говорят другие ответы, для примитивного типа этого примера не имеет значения)

Таким образом, это менее подвержено ошибкам, чем оба варианта в вопросе:

#include <iostream>
class Test3
{
    public: // Made public so we can easily output the vars. No struct is used to be consistent with question.
        int a { 4 }; // No need for separate initialization in constructor.
        int b { 2 }; // No need for separate initialization in constructor.
};
int main()
{
    const auto t = std::move(Test3()); // Implicitly-declared default constructor + implicitly-declared move assignment operator
    std::cout << t.a << t.b;
}

Вывод - 42.