Я пишу диспетчер памяти для своей виртуальной машины на С++. Ну, точнее, инструкции VM будут скомпилированы в С++ с встроенным менеджером памяти. Мне гораздо удобнее обрабатывать C, но теперь мне нужна встроенная поддержка обработки исключений, в значительной степени единственная причина, по которой я использую С++.
И C и С++ имеют строгое правило сглаживания, что два объекта несовместимых типов не должны пересекаться, с небольшим исключением в C для объединений. Но для определения поведения функций выделения памяти, таких как malloc
, calloc
, alloca
и т.д., Стандарт C имеет следующий абзац.
6.5-6 Эффективным типом объекта для доступа к его сохраненному значению является объявленный тип объекта, если таковой имеется. Выделенные объекты не имеют объявленного типа. Если значение хранится в объекте, не имеющем объявленный тип через lvalue, имеющий тип, который не является символом тип, то тип lvalue становится эффективным типом объект для этого доступа и для последующих обращений, которые не изменяют сохраненное значение. Если значение копируется в объект, не имеющий объявленного типа, используя memcpy
или memmove
, или копируется как массив типа символа, тогда эффективный тип измененного объекта для этого доступа и для последующих обращений, которые не изменяются значение - это эффективный тип объекта, из которого копируется значение, если оно есть. Для всех других доступов к объекту, не имеющему объявленного типа, эффективным типом объекта является просто тип lvalue, используемый для доступа.
Это эффективно делает использование необработанной выделенной памяти для любого типа хорошо определенным поведением в C. Я попытался найти аналогичный абзац в стандартном документе С++, но не смог его найти. Я думаю, что у С++ есть другой подход в этом отношении. Что такое эквивалент С++ "выделенный объект, не имеющий объявленного типа" в C, и как его определяет стандарт С++?
Ответ 1
Я думаю, что подход С++ в этом отношении можно суммировать так: "malloc()
является злом. Вместо этого используйте new
. Нет такого объекта, как объект без объявленного типа". Конечно, реализации С++ должны определять глобальный operator new()
, который в основном является версией С++ malloc()
(и которая может быть предоставлена пользователем). Само существование этого оператора доказывает, что в С++ есть что-то вроде объектов без объявленного типа, но стандарт не допустит этого.
Если бы я был вами, я бы принял прагматичный подход. И глобальные operator new()
, и malloc()
доступны на С++, поэтому любая реализация должна иметь возможность использовать свои возвращаемые значения разумно. Особенно malloc()
будет вести себя одинаково в C и С++. Таким образом, просто обработайте эти нетипизированные объекты так же, как вы бы обрабатывали их на C, и вы должны быть в порядке.
Ответ 2
Для С++ это описано в Object lifetime [object.life]; в частности:
Время жизни объекта типа T
начинается, когда:
- получено хранилище с правильным выравниванием и размером для типа
T
и - Если объект имеет нетривиальную инициализацию, его инициализация завершена.
Срок службы продолжается до тех пор, пока хранилище не будет повторно использовано или объект не будет уничтожен:
Программа может завершить время жизни любого объекта, повторно используя хранилище, которое объект занимает или явно вызов деструктора для объекта типа класса с нетривиальным деструктором.
Это имеет довольно странное значение, что неиспользуемое выделенное хранилище (возвращается из operator new
) содержит объект любого тривиального типа, который помещается в этот блок хранения, по крайней мере, до тех пор, пока не будет использован блок хранения. Но тогда стандарт больше заботится о том, чтобы лечить нетривиальные типы, чем эта незначительная бородавка.
Ответ 3
Я думаю, что "выделенный объект без объявленного типа" - это фактически выделенное хранилище, которое еще не инициализировано и в этом пространстве памяти еще не создан объект. Из глобальной функции распределения ::operator new(std::size_t)
и семейства (§ 3.7.4/2);
§3.7.4.1/2
Функция распределения пытается выделить запрошенный объем памяти. Если он успешный, он должен вернуть адрес начала блока хранения, длина которого в байтах должна быть не меньше размера запрашиваемого. Нет ограничений на содержимое выделенного хранилища при возврате из функции выделения.
Создание объекта, будь то автоматическое или динамическое, определяется двумя этапами, самим распределением, а затем конструкцией объекта в этом пространстве.
§3.8/1
Время жизни объекта - это свойство времени выполнения объекта. Говорят, что объект имеет непустую инициализацию, если он имеет тип класса или агрегата, и он или один из его членов инициализируется конструктором, отличным от тривиального конструктора по умолчанию. [Примечание: инициализация тривиальным конструктором copy/move - это непустая инициализация. - end note] Время жизни объекта типа T
начинается, когда:
- получено хранилище с правильным выравниванием и размером для типа
T
и - Если объект имеет нетривиальную инициализацию, его инициализация завершена.
Соответственно,
Время жизни объекта типа T заканчивается, когда:
- Если
T
- это тип класса с нетривиальным деструктором (12.4), начинается вызов деструктора или - хранилище, которое объект занимает, повторно используется или освобождается.
С++ WD n4527.
Ответ 4
Я не уверен, что такой аналог существует или нужен в С++. Чтобы выделить память, вы можете сделать одну из трех вещей
Foo f;
Это будет выделять sizeof(Foo)
количество памяти в стеке. Размер известен во время компиляции, так как компилятор знает, сколько места выделяется. То же самое относится к массивам и т.д.
Другой вариант -
Foo* f = new Foo; // or the smart pointer alternatives
Это будет выделено из кучи, но опять же sizeof(Foo)
должно быть известно, но это позволяет выделить во время выполнения.
Третий вариант, как @BasileStarynkevitch упоминает, размещение нового
Вы можете видеть, что все эти механизмы распределения в С++ требуют знания того типа, который вы выделяете для.
Хотя в С++ можно использовать malloc
и т.д., он нахмурился, поскольку он идет против зерна для типичной семантики С++. Вы можете увидеть обсуждение аналогичного вопроса. Другие механизмы выделения "сырой" памяти являются взломанными. Например
void* p = operator new(size);
Это будет выделять size
количество байтов.