Нестатические члены данных
Нестатические члены данных объявляются в спецификации членов класса.
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
};
Допускаются любые простые объявления, кроме следующих:
| (начиная с C++11) |
-
неполные типы, абстрактные типы классов и массивы таких типов не допускаются: в частности, класс
Cне может иметь нестатический член данных типаC, хотя он может иметь нестатический член данных типаC&(ссылка на C) илиC*(указатель наC); - нестатический член данных не может иметь то же имя, что и класс, если объявлен хотя бы один конструктор пользователем;
| (начиная с C++11) |
Кроме того, допускаются объявления битовых полей.
Размещение
При создании объекта некоторого класса C каждый нестатический член данных, не являющийся ссылочным типом, размещается в некоторой части представления объекта C. Занимают ли ссылочные члены какое-либо хранилище, определяется реализацией, однако их длительность хранения совпадает с длительностью хранения объекта, членами которого они являются.
| Для типов классов, не являющихся объединениями, ненулевого размера(начиная с C++20) члены , не разделённые спецификатором доступа(до C++11)с одинаковым доступом к членам(начиная с C++11) всегда размещаются так, что члены, объявленные позже, имеют более высокие адреса внутри объекта класса. Члены , разделённые спецификатором доступа(до C++11)с разными уровнями доступа(начиная с C++11) размещаются в неопределённом порядке (компилятор может сгруппировать их вместе). | (до C++23) |
| Для типов классов, не являющихся объединениями, члены ненулевого размера всегда размещаются так, что члены, объявленные позже, имеют более высокие адреса внутри объекта класса. Обратите внимание, что контроль доступа к членам по-прежнему влияет на свойство стандартного размещения (см. ниже). | (начиная с C++23) |
Требования к выравниванию могут потребовать добавления отступов между членами или после последнего члена класса.
Стандартное размещение
| Класс считается имеющим стандартное размещение и обладающим описанными ниже свойствами тогда и только тогда, когда он является POD-классом. | (до C++11) |
| Класс, все нестатические члены данных которого имеют одинаковый уровень доступа и для которого выполняются некоторые другие условия, называется классом со стандартным размещением (список требований см. в разделе класс со стандартным размещением). | (начиная с C++11) |
Общая начальная последовательность двух типов классов со стандартным размещением, не являющихся объединениями, — это самая длинная последовательность нестатических членов данных и битовых полей в порядке объявления, начинающаяся с первой такой сущности в каждом из классов, для которой выполняются следующие условия:
| (начиная с 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можно использовать для определения смещения любого члена относительно начала класса со стандартным размещением.
Инициализация членов
Нестатические члены данных можно инициализировать одним из двух способов:
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
}
Размер членов-массивов нельзя вывести из инициализаторов членов: 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) |
Использование
Имя нестатического члена данных или нестатической функции-члена может встречаться только в следующих трёх случаях:
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();
};
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
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
Примечания
| Макрос проверки поддержки функции | Значение | Стандарт | Функция |
|---|---|---|---|
__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]] | они не включаются |
См. также
| классы | |
| статические члены | |
| нестатические функции-члены | |
|
(C++11) | проверяет, является ли тип типом со стандартным размещением (шаблон класса) |
| смещение в байтах от начала типа со стандартным размещением до указанного члена (макрос функции) |
|
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/data_members