Spec-Zone.ru › C++

Нестатические данные-члены

Нестатические данные-члены объявляются в определении члена класса.

class S
{
    int n;              // non-static data member
    int& r;             // non-static data member of reference type
    int a[2] = {1, 2};  // non-static data member with default member initializer (C++11)
    std::string s, *ps; // two non-static data members
 
    struct NestedS
    {
        std::string s;
    } d5;               // non-static data member of nested type
 
    char bit : 2;       // two-bit bitfield
};

Разрешены любые простые объявления, за исключением

  • extern и register спецификаторы класса хранения не допускаются;
  • thread_local спецификатор класса хранения не допускается (но допускается для статических данных-членов);
(с C++11)
  • неполные типы, абстрактные типы классов и массивы из них не допускаются: в частности, класс C не может иметь нестатический данные-член типа C, хотя он может иметь нестатический данные-член типа C& (ссылка на C) или C* (указатель на C);
  • нестатический данные-член не может иметь то же имя, что и имя класса, если присутствует хотя бы один объявленный пользователем конструктор;
  • спецификатор типа-заместителя (т.е. auto, decltype(auto)(с C++14), имя шаблона класса, подлежащее выводу(с C++17), ограниченный заместитель(с C++20)) не может использоваться в объявлении нестатического данных-члена (хотя допускается для статических данных-членов, которые инициализируются в определении класса).
(с C++11)

Кроме того, допускаются объявления битовых полей.

Макет

При создании объекта некоторого класса C, каждый нестатический данные-член типа, не являющегося ссылкой, выделяется в некоторой части представления объекта C. Занимают ли члены-ссылки какое-либо место в памяти, определяется реализацией, но их срок хранения совпадает со сроком хранения объекта, в котором они являются членами.

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

(до C++23)

Для типов класса, не являющихся объединениями, члены ненулевого размера всегда выделяются так, что члены, объявленные позже, имеют более высокие адреса в объекте класса. Обратите внимание, что контроль доступа к члену всё ещё влияет на свойство стандартного макета (см. ниже).

(с C++23)

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

Стандартный макет

Класс считается стандартным макетом и обладает описанными ниже свойствами, если и только если он является POD-классом.

(до C++11)

Класс, в котором все нестатические данные-члены имеют одинаковый контроль доступа и удовлетворяют определённым другим условиям, называется классом со стандартным макетом (см. класс со стандартным макетом для списка требований).

(с C++11)

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

  • либо оба элемента объявлены с [[no_unique_address]] атрибутом, либо ни один из них,
(с C++20)
  • соответствующие элементы имеют типы, совместимые с макетом,
  • соответствующие элементы имеют одинаковые требования к выравниванию, и
  • либо оба элемента являются битами полей с одинаковой шириной, либо ни один из них не является битовым полем.
struct A { int a; char b; };
struct B { const int b1; volatile char b2; }; 
// A and B's common initial sequence is A.a, A.b and B.b1, B.b2
 
struct C { int c; unsigned : 0; char b; };
// A and C's common initial sequence is A.a and C.c
 
struct D { int d; char b : 4; };
// A and D's common initial sequence is A.a and D.d
 
struct E { unsigned int e; char b; };
// A and E's common initial sequence is empty

Два типа класса со стандартным макетом, не являющихся объединениями, называются совместимыми по макету, если они являются одним и тем же типом, игнорируя модификаторы cv, если таковые имеются, если они являются перечислениями (т.е. перечислениями с одним и тем же базовым типом), совместимыми по макету, или если их общая начальная последовательность состоит из всех нестатических данных-членов и битовых полей (в примере выше, A и B совместимы по макету).

Два стандартных объединения называются совместимыми по макету, если они имеют одинаковое количество нестатических данных-членов, и соответствующие нестатические данные-члены (в любом порядке) имеют типы, совместимые по макету.

Типы со стандартным макетом обладают следующими специальными свойствами:

  • В стандартном объединении с активным членом типа класса, не являющегося объединением, T1, разрешается читать нестатический данные-член m другого члена объединения типа класса, не являющегося объединением, T2, при условии, что m является частью общей начальной последовательности T1 и T2 (за исключением того, что чтение переменной volatile через не-volatile glvalue неопределено).
  • Указатель на объект типа класса со стандартным макетом может быть reinterpret_cast в указатель на его первый нестатический небитовое данных-член (если у него есть нестатические данные-члены) или в любой другой его подобъект базового класса (если таковые имеются), и наоборот. Другими словами, заполнение не допускается перед первым данных-членом типа со стандартным макетом. Обратите внимание, что правила жёсткого алиасирования всё ещё применяются к результату такого преобразования.
  • Макрос offsetof может использоваться для определения смещения любого члена от начала класса со стандартным макетом.

Инициализация членов

Нестатические данные-члены могут быть инициализированы одним из двух способов:

1) В списке инициализаторов членов конструктора.
struct S
{
    int n;
    std::string s;
    S() : n(7) {} // direct-initializes n, default-initializes s
};
2) С помощью инициализатора члена по умолчанию, который представляет собой фигурную или равную инициализацию, включённую в объявление члена и используется, если член опущен из списка инициализаторов члена конструктора.
struct S
{
    int n = 7;
    std::string s{'a', 'b', 'c'};
    S() {} // default member initializer will copy-initialize n, list-initialize s
};

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

#include <iostream>
 
int x = 0;
struct S
{
    int n = ++x;
    S() {}                 // uses default member initializer
    S(int arg) : n(arg) {} // uses member initializer 
};
 
int main()
{
    std::cout << x << '\n'; // prints 0
    S s1;                   // default initializer ran
    std::cout << x << '\n'; // prints 1
    S s2(7);                // default initializer did not run
    std::cout << x << '\n'; // prints 1
}

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

(до C++20)

Члены типа массива не могут вывести свой размер из инициализаторов членов:

struct X
{
    int a[] = {1, 2, 3};  // error
    int b[3] = {1, 2, 3}; // OK
};

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

struct node
{
    node* p = new node; // error: use of implicit or defaulted node::node() 
};

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

struct A
{
    A() = default;     // OK
    A(int v) : v(v) {} // OK
    const int& v = 42; // OK
};
 
A a1;    // error: ill-formed binding of temporary to reference
A a2(1); // OK (default member initializer ignored because v appears in a constructor)
         // however a2.v is a dangling reference
(с C++11)

Если член-ссылка инициализируется из своего значения по умолчанию(до C++20)член имеет значение по умолчанию(с C++20) и подвыражение, которое может быть вычислено, является вычисляемым агрегатным инициализацией, которая бы использовала это значение по умолчанию, программа некорректна:

struct A;
extern A a;
 
struct A
{
    const A& a1{A{a, a}}; // OK
    const A& a2{A{}};     // error
};
 
A a{a, a};                // OK
(с C++17)

Использование

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

1) В качестве части выражения доступа к члену класса, который либо имеет этот член, либо унаследован от класса, имеющего этот член, включая неявные this-> выражения доступа к членам, которые появляются, когда имя нестатического члена используется в любом контексте, где разрешено this (внутри тел функций-членов, в списках инициализаторов членов, в значениях по умолчанию для инициализаторов членов в классе).
struct S
{
    int m;
    int n;
    int x = m;            // OK: implicit this-> allowed in default initializers (C++11)
 
    S(int i) : m(i), n(m) // OK: implicit this-> allowed in member initializer lists
    {
        this->f();        // explicit member access expression
        f();              // implicit this-> allowed in member function bodies
    }
 
    void f();
};
2) Для создания указателя на нестатический член.
struct S
{
    int m;
    void f();
};
 
int S::*p = &S::m;       // OK: use of m to make a pointer to member
void (S::*fp)() = &S::f; // OK: use of f to make a pointer to member
3) (только для членов данных, а не функций-членов) При использовании в невычисляемых операндах.
struct S
{
    int m;
    static const std::size_t sz = sizeof m; // OK: m in unevaluated operand
};
 
std::size_t j = sizeof(S::m + 42); // OK: even though there is no "this" object for m
Примечания: такие использования разрешены благодаря решению CWG issue 613 в N2253, которое некоторыми компиляторами (например, clang) рассматривается как изменение в C++11.

Примечания

Макрос проверки функций Значение Стандарт Функция
__cpp_nsdmi 200809L (C++11) Инициализаторы нестатических членов данных
__cpp_aggregate_nsdmi 201304L (C++14) Агрегатные классы с инициализаторами членов по умолчанию

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

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

DR Применен к Поведение, опубликованное Корректное поведение
CWG 80 C++98 все члены данных не могут иметь то же имя,
что и имя класса (нарушает совместимость с C)
разрешить нестатическим членам данных
иметь то же имя, что и класс, если нет
пользовательского конструктора
CWG 190 C++98 при определении совместимости компоновки,
все члены учитывались
учитываются только нестатические
члены данных
CWG 613 C++98 невычисляемые использования нестатических членов данных не допускались такие использования разрешены
CWG 645 C++98 не было определено, совместимы ли битовые поля и
члены, не являющиеся битовыми полями, с точки зрения компоновки
не совместимы
CWG 1397 C++11 класс считался полным
в инициализаторах членов по умолчанию
инициализация по умолчанию не может инициировать
определение конструктора по умолчанию
CWG 1425 C++98 не было ясно, разделяет ли стандартный объект компоновки
тот же адрес с первым нестатическим членом данных или первым под-объектом базового класса
нестатический член данных,
если он присутствует, в противном случае под-объект базового класса, если он присутствует
CWG 1696 C++98 члены-ссылки могли быть инициализированы временными объектами
(срок жизни которых заканчивался в конце конструктора)
такая инициализация некорректна
CWG 1719 C++98 разные cv-квалифицированные типы не были совместимы с точки зрения компоновки cv-квалификаторы игнорируются, спецификация улучшена
CWG 2254 C++11 указатель на стандартный класс компоновки без членов данных может быть преобразован с помощью reinterpret_cast к его первому базовому классу может быть преобразован с помощью reinterpret_cast
к любому из его базовых классов
CWG 2583 C++11 общая последовательность инициализации не учитывала требования выравнивания учитывались

См. также

классы
статические члены
нестатические функции-члены
is_standard_layout
(C++11)
проверяет, является ли тип стандартным типом компоновки
(шаблон класса)
offsetof
смещение байтов от начала стандартного типа компоновки до указанного члена
(макрос функции)

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

Spec-Zone.ru

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