Namespaces
Variants

Constructors and member initializer lists

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Конструкторы — это нестатические функции-члены , объявленные с использованием специального синтаксиса декларатора; они используются для инициализации объектов своих типов классов.

Конструктор не может быть корутиной .

(since C++20)

Конструктор не может иметь явный параметр объекта .

(since C++23)

Содержание

Синтаксис

Конструкторы объявляются с использованием деклараторов функций-членов следующей формы:

class-name ( parameter-list  (необязательно) ) except  (необязательно) attr  (необязательно)
class-name - выражение-идентификатор, возможно, за которым следует список атрибутов , и (начиная с C++11) возможно, заключенное в пару круглых скобок
parameter-list - список параметров
except -

спецификация динамических исключений

(до C++11)

либо спецификация динамических исключений
либо спецификация noexcept

(начиная с C++11)
(до C++17)

спецификация noexcept

(начиная с C++17)
attr - (начиная с C++11) список атрибутов

Единственные спецификаторы, разрешенные в спецификаторах объявления объявления конструктора, это friend , inline , constexpr (начиная с C++11) , consteval (начиная с C++20) и explicit (в частности, возвращаемый тип не допускается). Обратите внимание, что cv- и ref-квалификаторы также не разрешены: семантика const и volatile для конструируемого объекта вступает в силу только после завершения конструктора наиболее производного класса.

Идентификаторное выражение class-name должно иметь одну из следующих форм:

  • Для классов выражение-идентификатор представляет собой injected-class-name непосредственно охватывающего класса.
  • Для шаблонов классов выражение-идентификатор представляет собой имя класса, которое обозначает current instantiation (до C++20) injected-class-name (начиная с C++20) непосредственно охватывающего шаблона класса.
  • В противном случае, идентификаторное выражение является квалифицированным идентификатором, терминальный неквалифицированный идентификатор которого представляет собой injected-class-name своего контекста поиска .

Список инициализации членов класса

Тело определения функции любого конструктора класса T , перед открывающей фигурной скобкой составного оператора, может включать список инициализации членов  , синтаксис которого представляет собой символ двоеточия : , за которым следует разделённый запятыми список одного или нескольких инициализаторов-членов , каждый из которых имеет следующий синтаксис:

инициализатор члена инициализатор (1)
инициализатор класса инициализатор (2)
инициализатор пакета классов инициализатор ... (3) (начиная с C++11)
1) Прямо инициализирует элемент данных с именем member с помощью initializer . member может называть только нестатические элементы данных.
2) Инициализирует объект класса с помощью initializer . class может указывать только следующие классы:
(начиная с C++11)
  • Прямой базовый класс или виртуальный базовый класс класса T . В этом случае соответствующий подобъект базового класса прямо инициализируется с помощью initializer .
3) Инициализирует несколько подобъектов базовых классов с использованием pack expansion .
member - идентификатор, который именует член данных
class - имя класса
class-pack - пакет, который раскрывается в ноль или более классов
initializer - инициализатор , который не начинается с =
struct S
{
    int n;
    S(int);       // объявление конструктора
    S() : n(7) {} // определение конструктора:
                  // ": n(7)" - это список инициализации
                  // ": n(7) {}" - это тело функции
};
S::S(int x) : n{x} {} // определение конструктора: ": n{x}" - это список инициализации
int main()
{
    S s;      // вызывает S::S()
    S s2(10); // вызывает S::S(int)
}

Объяснение

Конструкторы не имеют имен и не могут быть вызваны напрямую. Они вызываются при выполнении инициализации и выбираются согласно правилам инициализации. Конструкторы без спецификатора explicit являются преобразующими конструкторами . Конструкторы со спецификатором constexpr делают свой тип литеральным типом . Конструкторы, которые могут быть вызваны без аргументов, являются конструкторами по умолчанию . Конструкторы, принимающие другой объект того же типа в качестве аргумента, являются конструкторами копирования и конструкторами перемещения .

Перед тем как составной оператор, формирующий тело функции конструктора, начнет выполняться, завершается инициализация всех прямых базовых классов, виртуальных базовых классов и нестатических членов данных. Список инициализации членов - это место, где может быть задана нестандартная инициализация этих подобъектов. Для базовых классов, которые не могут быть инициализированы по умолчанию, и для нестатических членов данных, которые не могут быть инициализированы через default-инициализацию или через их default member initializer , если таковой имеется (since C++11) , таких как члены ссылочных и const-квалифицированных типов, должны быть указаны инициализаторы членов. (Заметьте, что default member initializers для нестатических членов данных инстанциаций шаблонов классов могут быть некорректными, если тип члена или инициализатор являются зависимыми.) (since C++11) Инициализация не выполняется для анонимных объединений или variant members , которые не имеют инициализатора члена или default member initializer (since C++11) .

Инициализаторы, где class указывает на виртуальный базовый класс, игнорируются при конструировании любого класса, который не является наиболее производным классом конструируемого объекта.

Имена, которые появляются в initializer , вычисляются в области видимости конструктора:

class X
{
    int a, b, i, j;
public:
    const int& r;
    X(int i)
      : r(a) // инициализирует X::r для ссылки на X::a
      , b{i} // инициализирует X::b значением параметра i
      , i(i) // инициализирует X::i значением параметра i
      , j(this->i) // инициализирует X::j значением X::i
    {}
};

Исключения, которые выбрасываются из инициализаторов членов, могут быть обработаны с помощью блока try функции .

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

struct S
{
    int n = 42;   // default member initializer
    S() : n(7) {} // will set n to 7, not 42
};
(since C++11)

Ссылочные члены не могут быть привязаны к временным объектам в списке инициализации членов:

struct A
{
    A() : v(42) {} // Ошибка
    const int& v;
};

Примечание: то же самое относится к default member initializer .

Операции во время конструирования и деструкции

Функции-члены (включая виртуальные функции-члены ) могут быть вызваны для объекта в процессе конструирования или разрушения. Аналогично, объект в процессе конструирования или разрушения может быть операндом typeid или dynamic_cast .

Однако, если эти операции выполняются во время любого из следующих вычислений, поведение не определено:

(начиная с C++26)
  • оценка списка инициализации членов до завершения всех member-initializer ов для базовых классов

Делегирующий конструктор

Если имя самого класса появляется как class-or-identifier в списке инициализации членов, то список должен состоять только из этого одного инициализатора члена; такой конструктор известен как делегирующий конструктор , а конструктор, выбранный единственным членом списка инициализации, является целевым конструктором .

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

Делегирующие конструкторы не могут быть рекурсивными.

class Foo
{
public: 
    Foo(char x, int y) {}
    Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char, int)
};

Наследуемые конструкторы

Смотрите using declaration .

(since C++11)

Порядок инициализации

Порядок инициализаторов членов в списке не имеет значения, фактический порядок инициализации следующий:

1) Если конструктор является конструктором наиболее производного класса, виртуальные базы инициализируются в порядке их появления при обходе объявлений базовых классов в глубину слева направо (слева направо относится к появлению в списках базовых спецификаторов).
2) Затем прямые базовые классы инициализируются в порядке слева направо, как они появляются в списке базовых спецификаторов этого класса.
3) Затем, нестатические члены данных инициализируются в порядке их объявления в определении класса.
4) Наконец, выполняется тело конструктора.

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

Примечания

Макрос тестирования возможностей Значение Стандарт Возможность
__cpp_delegating_constructors 200604L (C++11) Делегирующие конструкторы

Пример

#include <fstream>
#include <string>
#include <mutex>
struct Base
{
    int n;
};   
struct Class : public Base
{
    unsigned char x;
    unsigned char y;
    std::mutex m;
    std::lock_guard<std::mutex> lg;
    std::fstream f;
    std::string s;
    Class(int x) : Base{123}, // инициализация базового класса
        x(x),     // x (член) инициализируется значением x (параметр)
        y{0},     // y инициализируется значением 0
        f{"test.cc", std::ios::app}, // это происходит после инициализации m и lg
        s(__func__), // __func__ доступен, так как список инициализации является частью конструктора
        lg(m),    // lg использует m, который уже инициализирован
        m{}       // m инициализируется до lg, даже если он указан последним здесь
    {}            // пустой составной оператор
    Class(double a) : y(a + 1),
        x(y), // x будет инициализирован до y, его значение здесь неопределено
        lg(m)
    {} // инициализатор базового класса отсутствует в списке, он
       // инициализируется по умолчанию (не то же самое, что если бы использовался Base(), что является value-init)
    Class()
    try // блок function try начинается до тела функции, которое включает список инициализации
      : Class(0.0) // делегирующий конструктор
    {
        // ...
    }
    catch (...)
    {
        // исключение произошло при инициализации
    }
};
int main()
{
    Class c;
    Class c1(1);
    Class c2(0.1);
}

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Applied to Behavior as published Correct behavior
CWG 194 C++98 синтаксис декларатора конструктора допускал
не более одного спецификатора функции (например, конструктор
не мог быть объявлен inline explicit )
разрешено несколько
спецификаторов функции
CWG 257 C++98 не было указано, должна ли абстрактный класс
предоставлять инициализаторы членов для своих виртуальных базовых классов
указано как не требуемое
и такие инициализаторы членов
игнорируются во время выполнения
CWG 263 C++98 синтаксис декларатора конструктора
запрещал конструкторам быть дружественными функциями
разрешено конструкторам
быть дружественными функциями
CWG 1345 C++98 члены анонимных объединений без инициализаторов
членов по умолчанию были инициализированы по умолчанию
они не инициализируются
CWG 1435 C++98 значение "имя класса" в
синтаксисе декларатора конструктора было неясным
изменен синтаксис на специализированный
синтаксис декларатора функции
CWG 1696 C++98 ссылочные члены могли быть инициализированы временными объектами
(время жизни которых заканчивалось в конце конструктора)
такая инициализация
является некорректной

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
  • 11.4.5 Конструкторы [class.ctor]
  • 11.9.3 Инициализация базовых классов и членов [class.base.init]
  • Стандарт C++20 (ISO/IEC 14882:2020):
  • 11.4.4 Конструкторы [class.ctor]
  • 11.10.2 Инициализация базовых классов и членов [class.base.init]
  • Стандарт C++17 (ISO/IEC 14882:2017):
  • 15.1 Конструкторы [class.ctor]
  • 15.6.2 Инициализация базовых классов и членов [class.base.init]
  • Стандарт C++14 (ISO/IEC 14882:2014):
  • 12.1 Конструкторы [class.ctor]
  • 12.6.2 Инициализация базовых классов и членов [class.base.init]
  • Стандарт C++11 (ISO/IEC 14882:2011):
  • 12.1 Конструкторы [class.ctor]
  • 12.6.2 Инициализация базовых классов и членов [class.base.init]
  • Стандарт C++98 (ISO/IEC 14882:1998):
  • 12.1 Конструкторы [class.ctor]
  • 12.6.2 Инициализация базовых классов и членов [class.base.init]

Смотрите также