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

С++ - инициализация переменных в заголовке vs с помощью конструктора

Что касается следующего: существуют ли какие-либо причины для одного над другим или они примерно эквивалентны?

class Something
{
    int m_a = 0;
};

против

class Something
{
    int m_a;
    Something(int p_a);
};

Something::Something(int p_a):m_a(p_a){ ... };
4b9b3361

Ответ 1

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

class Something
{
    int m_a = 0;
};

Здесь вы указываете значение для инициализации, т.е. 0, во время компиляции.

class Something
{
    int m_a;
    Something(int p_a);
};

Something::Something(int p_a):m_a(p_a){ ... };

И здесь вы делаете это во время выполнения (или, возможно, во время выполнения), при значении p_a неизвестно до тех пор, пока не будет вызван конструктор.

Следующий фрагмент кода приближается к вашему первому примеру:

class Something
{
    int m_a;
    Something();
};

Something::Something() : m_a(0) { /* ... */ };

Что вы должны здесь рассмотреть, так это то, что в первом случае значение отображается непосредственно в определении класса. Это может создать ненужную зависимость. Что произойдет, если вам нужно изменить 0 на 1 позже? Отображение значения непосредственно в определении класса (и, как правило, в файле заголовка) может привести к перекомпиляции большого количества кода в ситуациях, когда другая форма инициализации позволит избежать этого, поскольку часть Something::Something() : m_a(0) будет аккуратно инкапсулирована в исходный файл и не отображается в файле заголовка:

// Something.h - stable header file, never changed
class Something
{
    int m_a;
    Something();
};

// Something.cpp - can change easily
Something::Something() : m_a(0) { /* ... */ };

Конечно, преимущества инициализации в классе могут значительно перевесить этот недостаток. Это зависит. Вы просто должны помнить об этом.

Ответ 2

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

Второе требуется, если инициализатор зависит от аргументов конструктора или в противном случае слишком сложно для инициализации в классе; и может быть лучше, если конструктор сложный, чтобы сохранить всю инициализацию в одном месте. (И это также необходимо, если вам нужно поддерживать компиляторы pre-С++ 11.)

Ответ 3

Первая форма является новой для С++ 11 и поэтому на данный момент не очень хорошо поддерживается, особенно если вам нужно поддерживать множество старых компиляторов.

В противном случае они должны быть примерно эквивалентными при наличии компилятора С++ 11.

Ответ 4

Разрабатывая ответ на христианский Хакл.

Первая форма позволяет инициализировать m_a и иметь значение по умолчанию c'tor в одно и то же время. Или вы даже можете быть явным в своем коде и определить конструктор с ключевым словом default:

class Something
{       
    int m_a = 0;

    // explicitly tell the compiler to generate a default c'tor
    Something() = default;
};

Во второй форме автоматически сгенерированный по умолчанию c'tor оставил бы m_a неинициализированным, поэтому, если вы хотите инициализировать жестко закодированное значение, вы должны написать свой собственный c'tor по умолчанию:

class Something
{
    int m_a;

    // implement your own default c'tor
    Something() : m_a(0) {}
};

Ответ 5

class Something
{
    int m_a = 0;
};

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

class Something
{
    int m_a(0);
};

Итак, делая

class Something
{
    int m_a;// (0) is moved to the constructor
public:
    Something(): m_a(0){}
};

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

Лично мне не нравится первая форма, потому что она выглядит как "объявление, а затем назначение", что является полным заблуждением.

Ответ 6

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

class Foo{

public: 
 Vector<double> vec1;
 .
 .
 .
  Vector<double> vecN;
}

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

Ответ 7

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

Смотрите пример ниже:

class_x_1.h:

class X
{
private:
    int x = 10;
public:
     int GetX();
};

class_x_2.h:

class X
{
private:
    int x = 20;
public:
    int GetX();
};

class_x.cpp:

#include "class_x_1.h" // implementation uses the version that initializes x with 10

int X::GetX()
{
    return x;
}

main.cpp:

#include "class_x_2.h" // main includes definition that initializes x with 20

#include <iostream>

int main()
{
    X x;
    std::cout << x.GetX() << std::endl;
    return 0;
}

Выход:

20

Как и ожидалось, он вернет 20, потому что это версия, которая инициализирует x с 20. Однако, если вы перейдете к реализации X, вы можете ожидать, что это будет 10.