MyStruct *array = (MyStruct *)malloc(100*sizeof(MyStruct));
Здесь вы ошибаетесь.
array
не является указателем на один или несколько объектов MyStruct
, независимо от того, какой тип вы ему присвоили. Возвращаемое значение из malloc
— это void*
. Правила C++ не позволяют вам неявно преобразовывать void*
в другие типы, поэтому вам пришлось поместить туда (MyStruct*)
. Сама потребность в явном приведении должна сказать вам, что вы делаете что-то подозрительное.
Правила C++ гласят, что если вы явно приводите void*
к некоторому Type*
(за пределами определенных специальных типов), это допустимо только, если void*
, для которого вы выполняете приведение, изначально был Type*
, который сам был брошен в void*
. Это не причина; этот void*
происходит от malloc
и никогда не был MyStruct*
. Вы лжете компилятору и тем самым провоцируете неопределенное поведение. Отсюда и сбой.
Если вам нужно определенное поведение, вам нужно на самом деле использовать C++, а не этот язык "Я не могу поверить, что это не C++", который вы изобретаете. Например:
void *block = malloc(100 * sizeof(MyStruct));
MyStruct* array = new(block) MyStruct[100];
Обратите внимание на полное отсутствие здесь операций приведения.
Конечно, удаление этого массива — это боль:
for(int i = 99; i >= 0; --i)
array[i].~MyStruct();
free(block);
Обратите внимание, что вам нужно уничтожить их в обратном порядке, в порядке, обратном тому, в котором они были построены.
интересно, как эти вещи вообще работают внутри. делает ли new[] дополнительную память, говорящую моему процессору о чем-то, чего нет у malloc? и почему/что/как имитировать без new[] ? или новое размещение? как мне написать это в необработанном двоичном коде для процессора? это вообще возможно? что именно здесь делает волшебство new[]?
Все это зависит от реализации. Что именно происходит, что касается языка, четко определено. Размещение new, среди прочего, вызовет конструктор объекта. Новое размещение в массиве вызовет конструкторы для всех объектов в массиве в порядке от первого до последнего. Если один из них выбросит исключение, то он вызовет деструктор для любых ранее созданных объектов, а затем выдаст исключение.
То, что именно делается, зависит от реализации, не более того, как именно реализовано наследование и так далее. Очевидно, что компилятор генерирует для этого некоторый код, но опять же, именно то, что генерируется, зависит от реализации.
То, как вы бы «написали это в необработанном двоичном коде для ЦП», невозможно, если вы на самом деле пишете С++. Чтобы реализовать новое размещение, вы должны иметь возможность вызывать конструктор класса. И... именно для этого для предназначено новое место размещения. Вам не разрешено получать даже указатель члена на конструктор (и даже если бы вы могли, содержимое указателей членов зависит от реализации, и они не всегда будут голым указателем на какую-то функцию сборки). Таким образом, нет никакого способа даже идентифицировать код конструктора без присяжных, специфичных для платформы.
Вы можете узнать, как это сделать для конкретной системы, посмотрев в сгенерированной сборке вызов для размещения new. Но на каждом компиляторе он будет другим.
person
Nicol Bolas
schedule
01.06.2012