Куча (структура данных)
- 1 year ago
- 0
- 0
Простая структура данных
(
англ.
plain old data
,
POD
) —
тип данных
в современных
высокоуровневых языках программирования
имеющий жёстко определённое расположение полей в памяти, не требующий ограничения доступа и автоматического
управления
. Переменные такого типа можно воссоздавать простыми процедурами копирования участков памяти наподобие
memcpy
. Противоположность —
управляемая структура данных
.
Проще всего определить простую структуру данных от противного. Например, если компилятор скрытно от пользователя переставил поля местами или при создании структуры данных скрытно вызывает конструктор , или при уничтожении структуры вызывает деструктор , или при копировании — особую процедуру копирования, то это управляемая (то есть, не простая) структура.
Простые структуры данных имеют две особенности.
Компилятор может автоматически перестроить структуру данных по своему усмотрению (например, изменить порядок полей. В языке C++ это возможно только в случае, если между полями есть метка доступа public/private/protected. Последовательность полей, не разделенных такой меткой, обязана размещаться в памяти в порядке объявления полей). Подобная перестройка может серьёзно сэкономить память, но нарушает совместимость. В POD’ах такая оптимизация отключена.
Другими словами: типы, отмеченные как POD, устроены в памяти в точности так, как описал программист (возможно, с некоторым выравниванием ). Поэтому только POD’ы можно использовать для связи между двумя библиотеками времени выполнения . В частности — для передачи данных из программы в программу, из плагина в плагин, для связи с кодом, написанным на другом языке программирования . Чтобы быстро записать на диск сложный заголовок файла наподобие BMP , можно сформировать его в памяти, а затем записать одной командой — но структура данных, в которой формируем заголовок, также должна быть POD’ом.
Это значит, что при появлении объекта не нужно вызывать конструктор, при копировании — операцию присваивания, а при уничтожении — деструктор. Это, в свою очередь, даёт следующие преимущества:
memcpy
.
union
(в Паскале соответственно
record/case
).
GetLastError
) плохо совместимы с автоматически управляемыми типами.
В C++ POD определяется от противного. Тип данных является POD’ом, если:
operator=
, принимающего на входе тот же тип);
private
и
protected
;
По стандарту C++ простой тип данных устроен в точности так, как описано (и полностью совместим побайтно по раскладке в памяти со структурой C). Управляемую же структуру компилятор может реорганизовать так, как он сочтёт наиболее эффективным.
Определение POD до C++ 11:
Агрегатом называется либо массив, либо же класс, не имеющий:
Агрегат можно инициализировать (как в Си) списком вида = {1, 2, 3};
Скаляром называется:
(то есть тип, который не есть класс, массив или ссылка)
PODом называется либо скаляр , либо массив других POD’ов, либо класс, который является агрегатом, и кроме того:
«Предсказуемое устройство в памяти» и «отсутствие управляющего кода» — сходные, но разные свойства типа. Например, структуру данных
STRRET
,
которая в Windows служит для передачи строк из одного
менеджера памяти
в другой, можно «
обернуть
» в управляющий код, но второе свойство — предсказуемое устройство — остаётся. Поэтому концепция POD’ов в C++11 разделена на три.
Класс называется «имеющим тривиальный конструктор копирования», если верно все перечисленное ниже:
Автосгенерированный тривиальный конструктор копирования есть memmove().
Точно таким же образом определяются понятия «имеющий тривиальный конструктор умолчания/оператор присваивания/конструктор перемещения/оператор перемещения».
Класс называется «имеющим тривиальный деструктор», если верно все перечисленное ниже:
Такой класс не требует разрушения, и содержащую его память можно освобождать без очистки.
Класс называется «тривиально копируемым», если у него тривиальны все вышеперечисленные специальные функции-члены (кроме конструктора умолчания, который может быть нетривиален). Скаляры, а также массивы тривиально копируемых объектов, тоже тривиально копируемы. Такие типы могут копироваться через
memcpy
.
Класс называется «тривиальным», если он тривиально копируем и у него к тому же тривиален конструктор умолчания.
Иными словами, класс является тривиальным , если у него тривиальны:
T()
;
T(T&)
;
T(T&&)
;
~T()
;
operator=(T&)
;
operator=(T&&)
.
Класс является типом со стандартным устройством , если:
private
, все
protected
или все
public
).
Поясним последнее условие: в языке не может быть двух разных объектов одного типа с одинаковым адресом, из чего следует, что размер пустого (без нестатических полей) класса не может быть 0 (как минимум 1). Однако для «части B в классе class D : B» сделано исключение, и её размер (если она пуста) может строго равняться нулю, что приводит к отсутствию каких-либо «прокладок» между началом D и его первым полем. Но при этом, если тип первого поля тоже B, исключение применяться не может, ибо (B*)&d и &(d.field1) указывают на разные объекты одного и того же типа, и потому «прокладка» нужна. Последнее условие из списка выше означает не более чем «в классах стандартного устройства таковая прокладка запрещена».
У таких типов предсказуемое устройство в памяти (например, адрес объекта как целого совпадает с адресом его первого поля, естественно, после reinterpret_cast в один и тот же тип, например в void*), их можно передавать в другую библиотеку времени выполнения и в другие языки программирования.
Тогда POD — это массив других PODов, или скаляр, или тривиальный класс со стандартным устройством, все нестатические поля которого тоже POD’ы.
Для работы с константами, вычисляемыми при компиляции, и статической инициализации в C++11 есть более мягкое понятие — литеральный тип . А именно:
T()
, либо какой-нибудь конструктор (кроме конструктора копирования и перемещения) отмечен как
constexpr
;
T(T&)
тривиальный;
T(T&&)
тривиальный или запрещён;
~T()
;
Начиная с C++ 03, существует разница между записями T t; и T t();, а равно между new T и new T().
Версия с пустыми скобками называется «инициализация значением», а без них — «инициализация по умолчанию».
Инициализация по умолчанию: если конструктор умолчания тривиален, то не делается ничего, в объекте остается мусор. Если же конструктор умолчания нетривиален, то он исполняется.
Инициализация значением: если есть явно написанный конструктор умолчания, то он исполняется. Если же нет (то есть если конструктор умолчания тривиален или же сгенерирован автоматически), то объект сначала зануляется, и только потом исполняется конструктор (если он нетривиален). Скалярные типы при инициализации значением зануляются.
Простыми структурами данных считаются все типы, кроме:
AnsiString
,
WideString
,
UnicodeString
). Впрочем, если не задевать скрытые управляющие поля, а работать только с данными,
System.Copy
использовать можно — не забывая, конечно, что несколько строк могут ссылаться на одну память и прежде надо вызвать функцию
UniqueString
;
class
. Впрочем,
TObject
,
TButton
и т. д. — это указатели на объект и
всегда являются
простыми типами!