Нестатические элементы данных
Нестатические элементы данных объявляются в спецификации элемента класса.
class S {int n;// нестатический элемент данныхint& r;// нестатический элемент данных ссылочного типаint a[2]={1, 2};// нестатический элемент данных с инициализатором элемента// по умолчанию (C++11)std::string s, *ps;// два нестатических элемента данныхstruct NestedS {std::string s;} d5;// нестатический элемент данных вложенного типаchar bit :2;// двухбитное битовое поле};
Разрешены любые простые объявления, кроме
| (начиная с C++11) |
- неполные типы, типы абстрактных классов и их массивы не допускаются: в частности, класс
C
не может иметь нестатический элемент данных типаC
, хотя он может иметь нестатический элемент данных типаC&
(ссылка на C) илиC*
(указатель на C); - нестатический элемент данных не может иметь то же имя, что и имя класса, если присутствует хотя бы один объявленный пользователем конструктор;
| (начиная с C++11) |
Кроме того, разрешены объявления битовых полей.
Содержание |
[править]Компоновка
Когда создаётся объект некоторого класса C
, каждый нестатический элемент данных не ссылочного типа распределяется в некоторой части объектного представления C
. Занимают ли ссылочные элементы какую-либо память, определяется реализацией, но их длительность хранения такая же, как у объекта, элементами которого они являются.
Для типов классов, отличных от union, элементы ненулевого размера(начиная с C++20), не разделённые спецификатором доступа(до C++11), с одинаковым доступом к элементу(начиная с C++11), всегда распределяются таким образом, чтобы элементы, объявленные позже, имели более высокие адреса внутри объекта класса. Элементы, разделённые спецификатором доступа(до C++11), с разным контролем доступа(начиная с C++11) распределяются в неопределенном порядке (компилятор может сгруппировать их вместе). Требования к выравниванию могут потребовать заполнения между элементами или после последнего элемента класса. | (до C++23) |
Для типов классов, отличных от union, всегда выделяются элементы ненулевого размера, так что элементы, объявленные позже, имеют более высокие адреса внутри объекта класса. Обратите внимание, что контроль доступа к элементу по-прежнему влияет на свойство стандартной компоновки (смотрите ниже). | (начиная с C++23) |
[править]Стандартная компоновка
Класс считается стандартным и имеет свойства, описанные ниже, тогда и только тогда, когда он является классом POD. | (до C++11) |
Класс, в котором все нестатические элементы данных имеют одинаковый контроль доступа и выполняются некоторые другие условия, называется классом стандартной компоновки (список требований смотрите в разделе класс со стандартным выравниванием). | (начиная с C++11) |
Общая начальная последовательность двух стандартных классовых типов, не являющихся объединением, представляет собой самую длинную последовательность нестатических элементов данных и битовых полей в порядке объявления, начиная с первого такого объекта в каждом из классов, так что
| (начиная с C++20) |
- соответствующие объекты имеют совместимые по выравниванию типы,
- соответствующие объекты имеют одинаковые требования к выравниванию, и
- либо оба объекта являются битовыми полями одинаковой ширины, либо ни один из них не является битовым полем.
struct A {int a;char b;};struct B {constint b1;volatilechar b2;};// общая начальная последовательность A и B это A.a, A.b и B.b1, B.b2struct C {int c;unsigned:0;char b;};// общая начальная последовательность A и C это A.a и C.cstruct D {int d;char b :4;};// общая начальная последовательность A и D это A.a и D.dstruct E {unsignedint e;char b;};// общая начальная последовательность A и E пуста
Два типа класса не объединения со стандартной компоновкой называются совместимыми по компоновке, если они относятся к одному типу, игнорируя cv-квалификаторы, если таковые есть, совместимы по компоновке перечислений (то есть перечисления с одним и тем же базовым типом), или если их общая начальная последовательность состоит из нестатических элементов данных и битовых полей (в приведённом выше примере, A
и B
совместимы по компоновке)
Два объединения со стандартной компоновкой называются совместимыми-по-компоновке, если они имеют одинаковое количество нестатических элементов данных, а соответствующие нестатические элементы данных (в любом порядке) имеют типы, совместимые с компоновкой.
Стандартные типы компоновок обладают следующими особыми свойствами:
- В объединении стандартной компоновки с активным элементом типа класса не объединения
T1
, разрешено читать нестатический элемент данныхm
другого элемента объединения, не являющегося типом класса объединенияT2
при условии, чтоm
является частью общей начальной последовательностиT1
иT2
(за исключением того, что чтение volatile элемента через не volatile glvalue не определено).
- В объединении стандартной компоновки с активным элементом типа класса не объединения
- Указатель на объект типа класса стандартной компоновки может быть reinterpret_cast в указатель на его первый нестатический элемент данных, не являющийся битовым полем (если он имеет нестатические элементы данных), или на любой другой подобъект его базового класса (если есть), и наоборот. Другими словами, заполнение не допускается перед первым элементом данных стандартного типа компоновки. Обратите внимание, что правила строгого псевдонима по-прежнему применяются к результату такого приведения.
- Макрос offsetof может использоваться для определения смещения любого элемента от начала класса стандартной компоновки.
[править]Инициализация элемента
Нестатические элементы данных могут быть инициализированы одним из двух способов:
struct S {int n;std::string s; S(): n(7){}// прямая инициализация n, инициализация по умолчанию s };
2) Через инициализатор элемента по умолчанию, который является фигурными скобками или равен инициализатору, включенному в объявление элемента, и используется, если этот элемент пропущен в списке инициализаторов элементов конструктора. struct S {int n =7;std::string s{'a', 'b', 'c'}; S(){}// инициализатор элемента по умолчанию будет инициализировать копией n,// инициализировать списком s }; Если элемент имеет инициализатор элемента по умолчанию и также появляется в списке инициализации элементов в конструкторе, инициализатор элемента по умолчанию игнорируется для этого конструктора. Запустить этот код #include <iostream> int x =0;struct S {int n =++x; S(){}// использует инициализатор элемента по умолчанию S(int arg): n(arg){}// использует инициализатор элемента}; int main(){std::cout<< x <<'\n';// печатает 0 S s1;// инициализатор по умолчанию выполненstd::cout<< x <<'\n';// печатает 1 S s2(7);// инициализатор по умолчанию не выполненstd::cout<< x <<'\n';// печатает 1}
Элементы типа массива не могут определить свой размер из инициализаторов элементов: struct X {int a[]={1,2,3};// ошибкаint b[3]={1,2,3};// OK}; Инициализаторам элементов по умолчанию не разрешается вызывать неявное определение заданного по умолчанию конструктора по умолчанию для включающего класса или спецификацию исключения этого конструктора : struct node { node* p = new node;// ошибка: использование неявного или заданного по умолчанию// node::node()}; Ссылочные элементы не могут быть привязаны к временным элементам в инициализаторе элементов по умолчанию (примечание; то же правило существует для списков инициализаторов элементов): struct A { A()=default;// OK A(int v): v(v){}// OKconstint& v =42;// OK}; A a1;// ошибка: неверно сформированная привязка временного объекта к ссылке A a2(1);// OK (инициализатор элемента по умолчанию игнорируется, поскольку v появляется// в конструкторе)// однако a2.v это висячая ссылка | (начиная с C++11) |
Если элемент ссылка инициализируется из его инициализатора элемента по умолчанию(до C++20)элемент имеет инициализатор элемента по умолчанию(начиная с C++20) и его потенциально оцениваемое подвыражение представляет собой агрегатную инициализацию, которая будет использовать этот инициализатор элемента по умолчанию, программа некорректна: struct A;extern A a;struct A {const A& a1{ A{a, a}};// OKconst A& a2{ A{}};// ошибка}; A a{a, a};// OK | (начиная с C++17) |
[править]Применение
Имя нестатического элемента данных или нестатической функции элемента может появляться только в следующих трёх ситуациях:
this
разрешён (внутри тел функций-элементов, в списках инициализаторов элементов, в инициализаторах элементов по умолчанию внутри класса). struct S {int m;int n;int x = m;// OK: неявный this-> разрешён в инициализаторах// по умолчанию (C++11) S(int i): m(i), n(m)// OK: неявный this-> разрешён в списках// инициализаторов элементов{ this->f();// явное выражение доступа к элементу f();// неявный this-> разрешён в телах функций-элементов}void f();};
struct S {int m;void f();};int S::*p =&S::m;// OK: использование m для создания указателя на элементvoid(S::*fp)()=&S::f;// OK: использование f для создания указателя на элемент
struct S {int m;staticconststd::size_t sz = sizeof m;// OK: m в неполном операнде}; std::size_t j = sizeof(S::m+42);// OK: хотя для m не существует объекта "this"
[править]Примечание
Макрос тест функциональности | Значение | Стандарт | Комментарий |
---|---|---|---|
__cpp_nsdmi | 200809L | (C++11) | Инициализаторы нестатических элементов данных |
__cpp_aggregate_nsdmi | 201304L | (C++14) | Агрегатные классы с инициализаторами элементов по умолчанию |
[править]Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
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 в любой из его базовых классов |
[править]Смотрите также
классы | |
статические элементы | |
нестатические функции-элементы | |
(C++11) | проверяет, является ли тип типом со стандартной компоновкой (шаблон класса) |
смещение в байтах от начала типа со стандартной компоновкой до указанного элемента (функция-макрос) |