Spec-Zone.ru › C++

Конструкторы и списки инициализации членов

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

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

(начиная с C++20)

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

(начиная с 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 должно иметь одну из следующих форм:

  • В объявлении друга идентификаторное выражение является квалифицированным идентификатором, который обозначает конструктор.
  • В противном случае, в объявлении члена, принадлежащем спецификации членов класса или шаблона класса:
    • Для классов идентификаторное выражение является внедрённым именем класса непосредственно охватывающего класса.
    • Для шаблонов классов идентификаторное выражение является именем класса, обозначающим текущую специализацию(до C++20)внедрённым именем класса(начиная с C++20) непосредственно охватывающего шаблона класса.
  • В противном случае идентификаторное выражение является квалифицированным идентификатором, конечный неквалифицированный идентификатор которого представляет собой внедрённое имя класса в контексте его поиска.

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

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

class-or-identifier ( expression-list (необязательно) ) (1)
class-or-identifier braced-init-list (2) (начиная с C++11)
parameter-pack ... (3) (начиная с C++11)
1) Инициализирует базовый класс или член, обозначенный class-or-identifier, с помощью прямой инициализации или, если expression-list пуст, с помощью инициализации значением
2) Инициализирует базовый класс или член, обозначенный class-or-identifier, с помощью инициализации списком (которая превращается в инициализацию значением, если список пуст, и в инициализацию агрегата при инициализации агрегата)
3) Инициализирует несколько базовых классов с помощью раскрытия пакета параметров
class-or-identifier - любой идентификатор, обозначающий нестатический член-данные, либо любое имя типа, обозначающее сам класс (для делегирующих конструкторов) или прямой либо виртуальный базовый класс.
expression-list - возможно, пустой список аргументов, разделённых запятыми, которые передаются конструктору базового класса или члена
braced-init-list - список инициализаторов в фигурных скобках
parameter-pack - имя пакета параметров вариативного шаблона
struct S
{
    int n;
    
    S(int);       // constructor declaration
    
    S() : n(7) {} // constructor definition:
                  // ": n(7)" is the initializer list
                  // ": n(7) {}" is the function body
};

S::S(int x) : n{x} {} // constructor definition: ": n{x}" is the initializer list

int main()
{
    S s;      // calls S::S()
    S s2(10); // calls S::S(int)
}

Пояснение

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

До начала выполнения составного оператора, образующего тело функции конструктора, завершается инициализация всех прямых базовых классов, виртуальных базовых классов и нестатических членов-данных. Список инициализации членов — это место, где можно задать нестандартную инициализацию этих подобъектов. Для базовых классов, которые нельзя инициализировать по умолчанию, и нестатических членов-данных, которые нельзя инициализировать посредством инициализации по умолчанию или их инициализатора по умолчанию для члена, если он есть(начиная с C++11), например членов ссылочного и const-квалифицированного типов, инициализаторы членов должны быть заданы. (Обратите внимание, что инициализаторы по умолчанию для нестатических членов-данных экземпляров шаблона класса могут быть некорректными, если тип члена или инициализатор являются зависимыми.)(начиная с C++11) Для анонимных объединений или вариантных членов, у которых нет инициализатора члена или инициализатора по умолчанию для члена(начиная с C++11), инициализация не выполняется.

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

Имена, встречающиеся в expression-list или braced-init-list, вычисляются в области видимости конструктора:

class X
{
    int a, b, i, j;
public:
    const int& r;
    X(int i)
      : r(a) // initializes X::r to refer to X::a
      , b{i} // initializes X::b to the value of the parameter i
      , i(i) // initializes X::i to the value of the parameter i
      , j(this->i) // initializes X::j to the value of X::i
    {}
};

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

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

struct S
{
    int n = 42;   // default member initializer
    S() : n(7) {} // will set n to 7, not 42
};
(начиная с C++11)

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

struct A
{
    A() : v(42) {} // Error
    const int& v;
};

Примечание: то же относится к инициализатору по умолчанию для члена.

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

Для объекта, который конструируется или разрушается, можно вызывать функции-члены (включая виртуальные функции-члены). Аналогичным образом объект, который конструируется или разрушается, может быть операндом 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.

(начиная с 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}, // initialize base class
        x(x),     // x (member) is initialized with x (parameter)
        y{0},     // y initialized to 0
        f{"test.cc", std::ios::app}, // this takes place after m and lg are initialized
        s(__func__), // __func__ is available because init-list is a part of constructor
        lg(m),    // lg uses m, which is already initialized
        m{}       // m is initialized before lg even though it appears last here
    {}            // empty compound statement
    
    Class(double a) : y(a + 1),
        x(y), // x will be initialized before y, its value here is indeterminate
        lg(m)
    {} // base class initializer does not appear in the list, it is
       // default-initialized (not the same as if Base() were used, which is value-init)
    
    Class()
    try // function try block begins before the function body, which includes init list
      : Class(0.0) // delegate constructor
    {
        // ...
    }
    catch (...)
    {
        // exception occurred on initialization
    }
};

int main()
{
    Class c;
    Class c1(1);
    Class c2(0.1);
}

Отчёты об ошибках

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

DR Применён к Поведение согласно опубликованной редакции Корректное поведение
CWG 194 C++98 синтаксис декларатора конструктора допускал
не более одного спецификатора функции (например, конструктор
нельзя было объявить как inlineexplicit)
разрешено несколько
спецификаторов функции
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]

См. также

  • устранение копирования
  • преобразующий конструктор
  • копирующее присваивание
  • конструктор копирования
  • конструктор по умолчанию
  • деструктор
  • explicit
  • инициализация
    • инициализация агрегата
    • постоянная инициализация
    • копирующая инициализация
    • инициализация по умолчанию
    • прямая инициализация
    • инициализация списком
    • инициализация ссылки
    • инициализация значением
    • нулевая инициализация
  • перемещающее присваивание
  • конструктор перемещения
  • new

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/constructor

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API