Spec-Zone.ru › C++

Объект

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

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

  • размер (определяется с помощью sizeof);
  • требования к выравниванию (определяются с помощью alignof);
  • продолжительность хранения (автоматическая, статическая, динамическая, локальная для потока);
  • срок жизни (ограничен продолжительностью хранения или временный);
  • тип;
  • значение (которое может быть неопределенным, например, для инициализированных по умолчанию типов, не являющихся классами);
  • необязательно, имя.

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

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

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

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

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

  • операциями, которые начинают срок жизни массива типа unsigned char или std::byte(с C++17), в этом случае такие объекты создаются в массиве,
  • вызовом следующих функций выделения памяти, в этом случае такие объекты создаются в выделенной памяти:
    • operator new (включая placement new)
    • operator new[] (включая placement new)
    • std::malloc
    • std::calloc
    • std::realloc
  • std::aligned_alloc
(с C++17)
  • вызовом следующих функций копирования представления объекта, в этом случае такие объекты создаются в целевом области памяти или результате:
    • std::memcpy
    • std::memmove
  • std::bit_cast
(с C++20)

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

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

#include <cstdlib>
 
struct X { int a, b; };
 
X* MakeX()
{
    // One of possible defined behaviors:
    // the call to std::malloc implicitly creates an object of type X
    // and its subobjects a and b, and returns a pointer to that X object
    X* p = static_cast<X*>(std::malloc(sizeof(X)));
    p->a = 1;
    p->b = 2;
    return p;
}

Вызов std::allocator::allocate или неявно определённые функции копирования/перемещения для типов объединения также могут создавать объекты. Функции std::start_lifetime_as и std::start_lifetime_as_array также неявно создают объекты указанного типа и массив указанного типа соответственно в позиции указателя, переданного им, и возвращают указатель на объект и на первый элемент массива соответственно.(с C++23)

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

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

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

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

Для TriviallyCopyable типов, представление значения является частью представления объекта, что означает, что копирование байтов, занимаемых объектом в памяти, достаточно для создания другого объекта с тем же значением (за исключением случая, если объект является потенциально перекрывающимся подобъектом или значение представляет собой запрещённое представление своего типа, и его загрузка в ЦП вызывает аппаратное исключение, например, SNaN (сигнализирующее не число) значения с плавающей точкой или NaT (нечто) целые числа).

Хотя большинство реализаций не допускают запрещённые представления, биты заполнения или несколько представлений для целочисленных типов, существуют исключения; например, значение целочисленного типа на Itanium может быть запрещённым представлением.

Обратное не обязательно верно: два объекта типа TriviallyCopyable с различными представлениями объекта могут представлять одно и то же значение. Например, несколько битовых шаблонов с плавающей точкой представляют одно и то же специальное значение NaN. Более часто биты заполнения могут быть добавлены для удовлетворения требований к выравниванию, размерам бит-полей и т. д.

#include <cassert>
 
struct S
{
    char c;  // 1 byte value
             // 3 bytes of padding bits (assuming alignof(float) == 4)
    float f; // 4 bytes value (assuming sizeof(float) == 4)
 
    bool operator==(const S& arg) const // value-based equality
    {
        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'; // modify some padding bits
    assert(s1 == s2); // value did not change
}

Для объектов типа char, signed char и unsigned char (если они не являются чрезмерными бит-полями), каждый бит представления объекта должен участвовать в представлении значения, и каждый возможный битовый шаблон представляет собой отличное значение (без битов заполнения, запрещённых битов или нескольких представлений).

Подобраъекты

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

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

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

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

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

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

  • a является под-объектом b, или
  • b предоставляет хранилище для a, или
  • существует объект c, где a вложен в c, и c вложен в b.

Любые два объекта с перекрывающимися сроками жизни (которые не являются бит-полями) гарантированно имеют разные адреса, если один из них не вложен в другой или если они являются подобраъектами разных типов в одном полном объекте, и один из них является под-объектом нулевого размера.

static const char c1 = 'x';
static const char c2 = 'x';
assert(&c1 != &c2); // same values, different addresses

Для класса,

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

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

Полиморфные объекты

Объекты типа класса, который объявляет или наследует по крайней мере одну виртуальную функцию, являются полиморфными объектами. Внутри каждого полиморфного объекта реализация хранит дополнительную информацию (в каждой существующей реализации это один указатель, если оптимизация не отключена), которая используется вызовами виртуальных функций и функциями RTTI (dynamic_cast и typeid), чтобы определить во время выполнения тип, с которым был создан объект, независимо от выражения, в котором он используется.

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

#include <iostream>
#include <typeinfo>
 
struct Base1
{
    // polymorphic type: declares a virtual member
    virtual ~Base1() {}
};
 
struct Derived1 : Base1
{
     // polymorphic type: inherits a virtual member
};
 
struct Base2
{
     // non-polymorphic type
};
 
struct Derived2 : Base2
{
     // non-polymorphic type
};
 
int main()
{
    Derived1 obj1; // object1 created with type Derived1
    Derived2 obj2; // object2 created with type Derived2
 
    Base1& b1 = obj1; // b1 refers to the object obj1
    Base2& b2 = obj2; // b2 refers to the object 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 для списка исключений и примеров.

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

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

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

(с C++11)

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

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

#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

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

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

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

Типы аллокаторов должны правильно обрабатывать типы с расширенным выравниванием.

(с C++11)

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

(с C++11)
(до C++20)

Примечания

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

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

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам 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 поведение создания объекта
в невыровненном хранилище было неясно
поведение в этом случае является
неопределённым
P0593R6 C++98 предыдущая модель объектов не поддерживала многие полезные идиомы, необходимые стандартной библиотекой, и не была совместима с эффективными типами в C добавлен неявный объект создания

См. также

Документация C для Объекта

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

Spec-Zone.ru

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