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)

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

  • если __has_cpp_attribute(no_unique_address) не является 0, ни одна из сущностей не объявлена с атрибутом [[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-члена через невольтильную 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 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 общая начальная последовательность не учитывала
требования к выравниванию
теперь учитывает
CWG 2759 C++20 общая начальная последовательность могла включать
члены, объявленные [[no_unique_address]]
они не включаются

См. также

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

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

Spec-Zone.ru

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