Object
Программы на C++ создают, уничтожают, ссылаются на, получают доступ и манипулируют объектами .
Объект в C++ имеет
-
размер (можно определить с помощью
sizeof); -
требование выравнивания (можно определить с помощью
alignof); - продолжительность хранения (автоматическая, статическая, динамическая, локальная для потока);
- время жизни (ограничено продолжительностью хранения или временное);
- тип ;
- значение (которое может быть неопределённым, например, для инициализированных по умолчанию неклассовых типов);
- опционально, имя .
Следующие сущности не являются объектами: значение, ссылка, функция, перечислитель, тип, нестатический член класса, шаблон, специализация шаблона класса или функции, пространство имён, пакет параметров и this .
Переменная — это объект или ссылка, не являющаяся нестатическим членом-данных, которая вводится объявлением .
Содержание
Создание объектовОбъекты могут быть явно созданы с помощью определений , new выражений , throw выражений , изменения активного члена union и вычисления выражений, требующих временных объектов . Созданный объект однозначно определяется при явном создании объекта. Объекты типов с неявным временем жизни также могут быть неявно созданы с помощью
Ноль или более объектов могут быть созданы в одной и той же области памяти, при условии что это обеспечивает программе определенное поведение. Если такое создание невозможно, например, из-за конфликтующих операций, поведение программы не определено. Если несколько таких наборов неявно созданных объектов обеспечивают программе определенное поведение, не специфицируется, какой именно набор объектов создается. Другими словами, неявно созданные объекты не обязаны быть однозначно определенными. После неявного создания объектов в указанной области памяти некоторые операции производят указатель на подходящий созданный объект . Подходящий созданный объект имеет тот же адрес, что и область памяти. Аналогично, поведение не определено, только если ни одно такое значение указателя не может обеспечить программе определенное поведение, и не указано, какое значение указателя производится, если существует несколько значений, обеспечивающих программе определенное поведение. #include <cstdlib> struct X { int a, b; }; X* MakeX() { // Один из возможных определенных вариантов поведения: // вызов std::malloc неявно создает объект типа X // и его подобъекты a и b, и возвращает указатель на этот объект X X* p = static_cast<X*>(std::malloc(sizeof(X))); p->a = 1; p->b = 2; return p; } Вызов std::allocator::allocate или неявно определенных специальных функций-членов копирования/перемещения union типов также может создавать объекты. Представление объекта и представление значенияНекоторые типы и объекты имеют представления объектов и представления значений , они определены в таблице ниже:
Биты в представлении объекта типа или объекта, которые не являются частью представления значения, называются битами заполнения . Для TriviallyCopyable типов представление значения является частью представления объекта, что означает, что копирования байтов, занимаемых объектом в памяти, достаточно для создания другого объекта с тем же значением (за исключением случаев, когда объект является потенциально перекрывающимся подобъектом, или значение является trap representation своего типа и его загрузка в процессор вызывает аппаратное исключение, такое как SNaN ("signalling not-a-number") для значений с плавающей точкой или NaT ("not-a-thing") для целых чисел). Хотя большинство реализаций не допускают trap-представлений, битов заполнения или множественных представлений для целочисленных типов, существуют исключения; например, значение целочисленного типа на Itanium может быть trap-представлением . Обратное не обязательно верно: два объекта TriviallyCopyable типа с различными представлениями объектов могут представлять одно и то же значение. Например, несколько битовых паттернов с плавающей запятой представляют одно и то же специальное значение NaN . Более распространённый случай — биты заполнения могут добавляться для удовлетворения требований выравнивания , размеров битовых полей и т.д. #include <cassert> struct S { char c; // 1-байтовое значение // 3 байта заполнения (предполагая alignof(float) == 4) float f; // 4-байтовое значение (предполагая sizeof(float) == 4) bool operator==(const S& arg) const // сравнение по значению { return c == arg.c && f == arg.f; } }; void f() { assert(sizeof(S) == 8); S s1 = {'a', 3.14}; S s2 = s1; reinterpret_cast<unsigned char*>(&s1)[2] = 'b'; // изменяем некоторые биты заполнения assert(s1 == s2); // значение не изменилось } Для объектов типа char , signed char и unsigned char (если они не являются переразмерными битовыми полями ), каждый бит представления объекта должен участвовать в представлении значения, и каждая возможная битовая комбинация представляет уникальное значение (не допускаются биты заполнения, ловушки или множественные представления). ПодобъектыОбъект может иметь подобъекты . К ним относятся
Объект, который не является подобъектом другого объекта, называется полным объектом . Если полный объект, подобъект-член или элемент массива имеет классовый тип , его тип считается наиболее производным классом , чтобы отличать его от классового типа любого подобъекта базового класса. Объект наиболее производного классового типа или объект неклассового типа называется наиболее производным объектом . Для класса,
называются его потенциально конструируемыми подобъектами . Размер
Подобъект является
потенциально перекрывающимся подобъектом
, если он является подобъектом базового класса
или нестатическим элементом данных, объявленным с атрибутом
Объект obj может иметь нулевой размер только при выполнении всех следующих условий:
Для объекта obj удовлетворяющего всем вышеуказанным условиям:
См. empty base optimization для получения более подробной информации. Любой объект, не являющийся битовым полем, с ненулевым размером должен занимать один или более байтов памяти, включая каждый байт, который занят (полностью или частично) любым из его подобъектов. Занимаемая память должна быть непрерывной, если объект является тривиально копируемым или стандартным по размещению (since C++11) типа. АдресЕсли объект не является битовым полем или подобъектом нулевого размера, адрес этого объекта — это адрес первого байта , который он занимает. Объект может содержать другие объекты, в этом случае содержащиеся объекты являются вложенными в предыдущий объект. Объект a вложен в другой объект b если выполняется любое из следующих условий:
Объект является потенциально неуникальным объектом если он представляет собой один из следующих объектов:
Для любых двух объектов, не являющихся битовыми полями, с пересекающимися временами жизни :
// символьные литералы всегда уникальны static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // всегда true // символ 'x', доступный через "r", "s" и "il" // может иметь одинаковый адрес (т.е. эти объекты могут использовать общую память) static const char (&r) [] = "x"; static const char *s = "x"; static std::initializer_list<char> il = {'x'}; const bool b2 = r != il.begin(); // неопределенный результат const bool b3 = r != s; // неопределенный результат const bool b4 = il.begin() != &test1; // всегда true const bool b5 = r != &test1; // всегда true Полиморфные объекты
Объекты классового типа, которые объявляют или наследуют хотя бы одну виртуальную функцию, являются полиморфными объектами. Внутри каждого полиморфного объекта реализация хранит дополнительную информацию (в каждой существующей реализации это один указатель, если не оптимизирован), которая используется
виртуальными функциями
и возможностями RTTI (
Для неполиморфных объектов интерпретация значения определяется из выражения, в котором объект используется, и решается на этапе компиляции.
Запустить этот код
#include <iostream> #include <typeinfo> struct Base1 { // полиморфный тип: объявляет виртуальный член virtual ~Base1() {} }; struct Derived1 : Base1 { // полиморфный тип: наследует виртуальный член }; struct Base2 { // не полиморфный тип }; struct Derived2 : Base2 { // не полиморфный тип }; int main() { Derived1 obj1; // объект1 создан с типом Derived1 Derived2 obj2; // объект2 создан с типом Derived2 Base1& b1 = obj1; // b1 ссылается на объект obj1 Base2& b2 = obj2; // b2 ссылается на объект obj2 std::cout << "Expression type of b1: " << typeid(decltype(b1)).name() << '\n' << "Expression type of b2: " << typeid(decltype(b2)).name() << '\n' << "Object type of b1: " << typeid(b1).name() << '\n' << "Object type of b2: " << typeid(b2).name() << '\n' << "Size of b1: " << sizeof b1 << '\n' << "Size of b2: " << sizeof b2 << '\n'; } Возможный вывод: Expression type of b1: Base1 Expression type of b2: Base2 Object type of b1: Derived1 Object type of b2: Base2 Size of b1: 8 Size of b2: 1 Строгий алиасинг
Обращение к объекту с использованием выражения типа, отличного от типа, с которым он был создан, во многих случаях является неопределённым поведением. Список исключений и примеров смотрите в разделе
ВыравниваниеКаждый тип объекта имеет свойство, называемое требование выравнивания, которое представляет собой неотрицательное целое значение (типа std::size_t, и всегда степень двойки), указывающее количество байтов между последовательными адресами, по которым могут размещаться объекты этого типа.
Каждый тип объекта накладывает свое требование выравнивания на каждый объект этого типа; более строгое выравнивание (с большим требованием выравнивания) может быть запрошено с помощью Чтобы удовлетворить требования выравнивания всех нестатических членов класса, заполняющие биты могут вставляться после некоторых его членов. Запустить этот код #include <iostream>
// objects of type S can be allocated at any address
// because both S.a and S.b can be allocated at any address
struct S
{
char a; // size: 1, alignment: 1
char b; // size: 1, alignment: 1
}; // size: 2, alignment: 1
// objects of type X must be allocated at 4-byte boundaries
// because X.n must be allocated at 4-byte boundaries
// because int's alignment requirement is (usually) 4
struct X
{
int n; // size: 4, alignment: 4
char c; // size: 1, alignment: 1
// three bytes of padding bits
}; // size: 8, alignment: 4
int main()
{
std::cout << "alignof(S) = " << alignof(S) << '\n'
<< "sizeof(S) = " << sizeof(S) << '\n'
<< "alignof(X) = " << alignof(X) << '\n'
<< "sizeof(X) = " << sizeof(X) << '\n';
}
Возможный вывод: alignof(S) = 1
sizeof(S) = 2
alignof(X) = 4
sizeof(X) = 8
Самое слабое выравнивание (наименьшее требование выравнивания) — это выравнивание Если запрос на определенное расширенное выравнивание в определенном контексте не поддерживается реализацией, программа считается некорректной (ill-formed).
ПримечанияОбъекты в C++ имеют иное значение, чем объекты в объектно-ориентированном программировании (ООП) :
В отчёте о дефекте
P0593R6
рассматривалось неявное создание объектов при создании массива байтов или вызове
функции выделения памяти
(которая может быть пользовательской и
Отчеты о дефектахСледующие отчеты о дефектах, изменяющих поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
Смотрите также
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||