Namespaces
Variants

Object

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Программы на C++ создают, уничтожают, ссылаются на, получают доступ и манипулируют объектами .

Объект в C++ имеет

Следующие сущности не являются объектами: значение, ссылка, функция, перечислитель, тип, нестатический член класса, шаблон, специализация шаблона класса или функции, пространство имён, пакет параметров и this .

Переменная — это объект или ссылка, не являющаяся нестатическим членом-данных, которая вводится объявлением .

Содержание

Создание объектов

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

Объекты типов с неявным временем жизни также могут быть неявно созданы с помощью

  • за исключением константного вычисления, операции, которые начинают время жизни массива типа unsigned char или std::byte (начиная с C++17) , в этом случае такие объекты создаются в массиве,
  • вызов следующих функций выделения памяти, в этом случае такие объекты создаются в выделенной памяти:
(начиная с C++17)
(начиная с C++20)
  • вызов следующих специфических функций, в случае которого такие объекты создаются в указанной области памяти:
  • std::start_lifetime_as
  • std::start_lifetime_as_array
(since C++23)

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

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

#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 типов также может создавать объекты.

Представление объекта и представление значения

Некоторые типы и объекты имеют представления объектов и представления значений , они определены в таблице ниже:

Сущность Объектное представление Знаковое представление
полный объектный тип T последовательность из N unsigned char объектов, занимаемых полным объектом типа T , не являющимся битовым полем , где N равно sizeof ( T ) набор битов в объектном представлении T , которые участвуют в представлении значения типа T
полный объект, не являющийся битовым полем, obj типа T байты obj , соответствующие объектному представлению T биты obj , соответствующие знаковому представлению T
объект битового поля bf последовательность из N битов, занимаемых bf , где N — ширина битового поля набор битов в объектном представлении bf , которые участвуют в представлении значения bf

Биты в представлении объекта типа или объекта, которые не являются частью представления значения, называются битами заполнения .

Для 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 (если они не являются переразмерными битовыми полями ), каждый бит представления объекта должен участвовать в представлении значения, и каждая возможная битовая комбинация представляет уникальное значение (не допускаются биты заполнения, ловушки или множественные представления).

Подобъекты

Объект может иметь подобъекты . К ним относятся

  • объекты-члены
  • подобъекты базовых классов
  • элементы массива

Объект, который не является подобъектом другого объекта, называется полным объектом .

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

Для класса,

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

Размер

Подобъект является потенциально перекрывающимся подобъектом , если он является подобъектом базового класса или нестатическим элементом данных, объявленным с атрибутом [[ no_unique_address ]] (начиная с C++20) .

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

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

Для объекта obj удовлетворяющего всем вышеуказанным условиям:

  • Если obj является подобъектом базового класса standard-layout (since C++11) типа без нестатических членов данных, он имеет нулевой размер.
  • В противном случае, реализационно-определено, при каких обстоятельствах obj имеет нулевой размер.

См. empty base optimization для получения более подробной информации.

Любой объект, не являющийся битовым полем, с ненулевым размером должен занимать один или более байтов памяти, включая каждый байт, который занят (полностью или частично) любым из его подобъектов. Занимаемая память должна быть непрерывной, если объект является тривиально копируемым или стандартным по размещению (since C++11) типа.

Адрес

Если объект не является битовым полем или подобъектом нулевого размера, адрес этого объекта — это адрес первого байта , который он занимает.

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

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

(since C++11)
  • Подобъект потенциально неуникального объекта.

Для любых двух объектов, не являющихся битовыми полями, с пересекающимися временами жизни :

  • Если выполняется любое из следующих условий, они могут иметь одинаковый адрес:
  • Один из них вложен в другой.
  • Любой из них является подобъектом нулевого размера, и их типы не являются подобными .
  • Оба являются потенциально неуникальными объектами.
  • В противном случае они всегда имеют различные адреса и занимают непересекающиеся байты памяти.
// символьные литералы всегда уникальны
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 ( dynamic_cast и typeid ) для определения во время выполнения типа, с которым был создан объект, независимо от выражения, в котором он используется.

Для неполиморфных объектов интерпретация значения определяется из выражения, в котором объект используется, и решается на этапе компиляции.

#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

Строгий алиасинг

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

Выравнивание

Каждый object type обладает свойством, называемым alignment requirement , которое представляет собой неотрицательное целочисленное значение (типа std::size_t , и всегда являющееся степенью двойки), обозначающее количество байтов между последовательными адресами, по которым могут размещаться объекты данного типа.

Требование выравнивания типа может быть запрошено с помощью alignof или std::alignment_of . Функция выравнивания указателей std::align может быть использована для получения подходящим образом выровненного указателя в некотором буфере. std::aligned_storage может быть использован для получения подходящим образом выровненного хранилища. (до C++23)

(начиная с C++11)

Каждый тип объекта накладывает свои требования к выравниванию на каждый объект этого типа ; более строгое выравнивание (с большим требованием к выравниванию) может быть запрошено с помощью alignas (начиная с C++11) . Попытка создать объект в памяти, которая не соответствует требованиям к выравниванию типа объекта, является неопределенным поведением.

Для удовлетворения требований выравнивания всех нестатических членов класса , биты заполнения могут быть вставлены после некоторых его членов.

#include <iostream>
// объекты типа S могут быть размещены по любому адресу
// потому что и S.a, и S.b могут быть размещены по любому адресу
struct S
{
    char a; // размер: 1, выравнивание: 1
    char b; // размер: 1, выравнивание: 1
}; // размер: 2, выравнивание: 1
// объекты типа X должны быть размещены по 4-байтным границам
// потому что X.n должен быть размещен по 4-байтным границам
// поскольку требование выравнивания для int (обычно) равно 4
struct X
{
    int n;  // размер: 4, выравнивание: 4
    char c; // размер: 1, выравнивание: 1
    // три байта заполнения
}; // размер: 8, выравнивание: 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

Наименьшее выравнивание (самое маленькое требование к выравниванию) — это выравнивание char , signed char и unsigned char , которое равно 1 ; наибольшее фундаментальное выравнивание любого типа определяется реализацией и равно выравниванию std::max_align_t (начиная с C++11) .

Фундаментальные выравнивания поддерживаются для объектов всех видов длительностей хранения.

Если выравнивание типа делается более строгим (большим), чем std::max_align_t с использованием alignas , такой тип называется типом с расширенным выравниванием . Тип с расширенным выравниванием или классовый тип, нестатический член данных которого имеет расширенное выравнивание, является сверхвыровненным типом .

Allocator типы обязаны корректно обрабатывать сверхвыровненные типы.

(since C++11)


Определяется реализацией, поддерживают ли new expressions и (until C++17) std::get_temporary_buffer сверхвыровненные типы.

(since C++11)
(until C++20)

Примечания

Объекты в C++ имеют иное значение, чем объекты в объектно-ориентированном программировании (ООП) :

Объекты в C++ Объекты в ООП
могут иметь любой объектный тип
(см. std::is_object )
должны иметь классовый тип
нет концепции "экземпляра" имеют концепцию "экземпляра" (и существуют механизмы вроде instanceof для определения отношения "является-экземпляром")
нет концепции "интерфейса" имеют концепцию "интерфейса" (и существуют механизмы вроде instanceof для определения реализации интерфейса)
полиморфизм должен быть явно включен через виртуальные члены полиморфизм всегда включен

В отчёте о дефекте P0593R6 рассматривалось неявное создание объектов при создании массива байтов или вызове функции выделения памяти (которая может быть пользовательской и constexpr ) во время константной оценки. Однако такое разрешение вызывало недетерминизм в константной оценке, что было нежелательно и нереализуемо в некоторых аспектах. В результате P2747R2 запретил такое неявное создание объектов в константной оценке. Мы намеренно рассматриваем это изменение как отчёт о дефекте, хотя вся статья таковой не является.

Отчеты о дефектах

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

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 633 C++98 переменные могли быть только объектами они также могут быть ссылками
CWG 734 C++98 не было указано, могут ли переменные, определенные
в одной области видимости, которые гарантированно имеют
одинаковое значение, иметь одинаковый адрес
адрес гарантированно будет
разным, если их времена жизни пересекаются,
независимо от их значений
CWG 1189 C++98 два подобъекта базового класса одного
типа могли иметь одинаковый адрес
они всегда имеют
различные адреса
CWG 1861 C++98 для переразмеренных битовых полей узких символьных
типов все биты объектного представления
все еще участвовали в значении представления
разрешает биты заполнения
CWG 2489 C++98 char [ ] не может предоставлять хранилище, но объекты
могли быть неявно созданы в его хранилище
объекты не могут быть неявно созданы
в хранилище char [ ]
CWG 2519 C++98 определение объектного представления не учитывало битовые поля учитывает битовые поля
CWG 2719 C++98 поведение создания объекта
в невыровненном хранилище было неясным
поведение является
неопределенным в этом случае
CWG 2753 C++11 было неясно, может ли базовый массив
списка инициализации разделять хранилище со строковым литералом
они могут разделять хранилище
CWG 2795 C++98 при определении того, могут ли два объекта с пересекающимися
временами жизни иметь одинаковый адрес, если любой из них является
подобъектом нулевого размера, они могли иметь схожие различные типы
разрешает только несхожие типы
P0593R6 C++98 предыдущая модель объектов не поддерживала многие
полезные идиомы, требуемые стандартной библиотекой,
и не была совместима с эффективными типами в C
добавлено неявное создание объектов

Смотрите также