Перейти к содержанию

Инициализация переменных

Переменная должна быть инициализирована при объявлении.

В С++ имеется большое количество способов инициализировать переменные, но все они сводятся к 3 формам:

  • По умолчанию (default)
  • Прямая (direct)
  • Копирующая (copy)

В С++11 добавили единую инициализацию (uniform) через фигурные скобки, решив часть проблем с круглыми скобками.

Прямая инициализация (рекомендуемая)

Прямая инициализация (direct initialization) позволяет явно задать значение при объявлении.

Пример:

int x( 42 ); // direct initialization
int y{ 42 }; // direct-list initialization (C++11)

Инициализация по умолчанию

Избегайте инициализации по умолчанию (default initialization) для локальных переменных POD типов. Такие переменные содержат неопределенное значение (undefined value). То есть несмотря на название default initialization, переменные по сути остаются неинициализированными.

Неправильно:

int i; // неопределенное значение
int* p = new int; // указатель на неопределенное значение

Правильно:

int i{ 0 }; // direct-list initialization (C++11)
int* p = new int{ 0 };

Если переменная инициализируется функцией через аргумент по ссылке, рекомендуется использовать прямую инициализацию.

Неправильно:

void init(int& x);

void foo() {
    int x;
    init(x);
    // do something...
}

Правильно:

void init(int& x);

void foo() {
    int x{ 0 }; // direct-list initialization (C++11)
    init(x);
    // do something...
}

Нулевая инициализация

Глобальные переменные, а также статические переменные функций по умолчанию инициализируются нулем (zero initialization).

Пример:

int global_var;        // 0
double global_d;       // 0.0
char global_c;         // '\0'
int global_arr[10];    // все элементы 0

void foo() {
    static int static_var;     // 0
    static double static_d;    // 0.0
}

Копирующая инициализация

Копирующая инициализация (copy initialization), использующая присваивание, досталась С++ в наследство от языка С.

Пример:

int x = 42;
int y = int( 42 );  // copy initialization
int y = int{ 42 };  // copy initialization (C++11)
auto y = int{ 42 }; // copy initialization (C++11)
int z = { 42 };     // copy-list initialization (C++11)
std::string s = "hello";
Widget w = other_widget;

Также рекомендуется использовать вместо нее прямую инициализацию.

Инициализация значением

Инициализация значением (value initialization, С++03) использует круглые или фигурные скобки (С++11) и инициализирует объект нулем для POD типов. Может использовать синтаксис либо прамой либо копирующей инициализации. Для улучшения восприятия кода рекомендуется избегать инициализацию значением и явно указывать нуль в скобках.

Неправильно:

int x(); // most vexing parse! Это объявление функции
int y{}; // direct-list initialization (С++11)
int n = int(); // copy initialization
int m = int{}; // copy-list initialization (C++11)

Правильно:

// direct initialization
int x{ 0 };
int y{ 0 };
int n{ 0 };
int m{ 0 };

Агрегатная инициализация

Аггрегатная инициализация (aggregate initialization) позволяет инициализировать объекты-агрегаты списком значений в фигурных скобках (также как массивы) без явного вызова конструктора.

До C++11 агрегатами были только POD-типы, но с C++11 определение расширилось:

  • Нет пользовательских конструкторов
  • Нет приватных/защищённых нестатических членов
  • Нет виртуальных функций
  • Нет виртуальных/приватных/защищённых базовых классов

Пример:

struct Point {
    int x;
    int y;
    int z;
};
Point p{1, 2, 3};        // direct-list initialization (C++11)
Point p2 = {4, 5, 6};    // aggregate initialization

Единая инициализация

Синтаксис единой инициализации (uniform initialization), также известной как инициализация в фигурных скобках (brace initialization) или списочная инициализация (list initialization), был принят в С++11 для унификации всех способов инициализации. В результате появились еще две формы (по идее заменяющие все остальные):

  • direct-list initialization
  • copy-list initialization

Синтаксис:

T x(...);    // direct-list initialization
T x = {...}; // copy-list initialization

Примеры:

int x{};                        // x = 0
int a{5};                       // фундаментальные типы
std::string s{"hello"};         // классы
std::vector<int> v{1, 2, 3};    // контейнеры
Point p{1, 2};                  // агрегаты
Widget w{1, 2, 3};              // вызов конструктора
int* ptr{nullptr};              // указатели

В новом коде следует использовать только uniform инициализацию.

Используйте auto

Ключевое слово auto позволяет избежать неинициализированных переменных:

auto a = int{5};                       // фундаментальные типы
auto s = std::string{"hello"};         // классы
auto v = std::vector<int>{1, 2, 3};    // контейнеры
auto p = Point{1, 2};                  // агрегаты
auto w = Widget{1, 2, 3};              // вызов конструктора
auto ptr = int*{nullptr};              // указатели