Re[2]: Нельзя определить размер динам. массива, хотя delete[
От: Андрей Тарасевич Беларусь  
Дата: 22.02.06 06:23
Оценка: 22 (3)
Здравствуйте, Яшин Евгений, Вы писали:

ЯЕ>Здравствуйте, Андрей Тарасевич, Вы писали:


АТ>> Если компилятору эта информация нужна только для того и только для того, чтобы вызвать правильное количество деструкторов, то сразу приходит на ум очевидная оптимизация: если хранимые в массиве объекты не имеют деструкторов (т.е. не являются экземплярами классов) или имеют тривиальные деструкторы (т.е. фактически тоже не имеют деструкторов), то знать количество элементов в массиве компилятору совершенно незачем, и формировать и хранить это количество нигде не надо. Эту оптимизацию используют многие компиляторы, включая MSVC и GCC.


ЯЕ>Попробую с вами не согласиться.

ЯЕ> Во первых вы тут сами себе противоречите — а если объекты имеют нетривиальные деструктора? Тогда размер нужен обязательно,

Совершенно верно. В этом случае размер нужен обязательно. И компилятор его хранит — именно в этом случае.

ЯЕ> в этом случае вы предлагаете хранить bool нужен размер / не нужен размер? Такая оптимизация не имеет смысла.


Ничего подобного я не предлагаю. Факт наличия нетривиального деструктора однозначно определяется статическим типом выражения, использованного в качестве аргумента 'delete[]', т.е. известен на стадии компиляции. На основе этой инофрмации компилятор просто генерирует правильную реализацию 'delete[]': либо "нетривиальную", читающую размер и вызывающую деструкторы перед освобождением памяти, либо "тривиальную", просто сразу освобождающую память.

ЯЕ> А во вторых, не верите мне, поверьте Александреску. "Современное проектирование на C++", №4.7. Там утверждается что компилятор в любом случае имеет инфу о размере удаляемого блока и более того, её можно получить с помощью специальной перегрузки оператора:


ЯЕ>
ЯЕ>    void operator delete(void* p, std::size_t size);
ЯЕ>


Вы путаете два разных размера.

Есть доступный RTL размер выделенного блока сырой памяти в байтах. Это — размер "низкого уровня" — прямой аналог того самого размера, который используется еще сишной функцией 'free()' для блока памяти, выделенного 'malloc' (а если честно — это и есть тот самый размер, унаследованный еще из С). Именно этот размер и передается в 'operator new' и 'operator delete'.

И есть размер выделенного массива в элементах. Это отдельный самостоятельный размер, который, для массивов элементов с нетривиальным деструктором, отдельно и самостоятельно хранится. Зачастую можно услышать, что этот второй размер можно получить из первого путем деления первого размера на sizeof элемента. Это в общем случае не верно. Функции выделения сырой памяти RTL имеют права по своему усмотрению выделять больше памяти, чем у них просили. Поэтому результат такого деления может оказаться неверным (большим, чем должно было бы быть). Поэтому размер массива в элементах хранится отдельно, дополнительно к размеру блока сырой памяти в байтах.

Таким образом, выделяя память через 'new int[8]', мы просим и получаем от RTL блок памяти в '8 * sizeof(int)' байтов или несколько больше и RTL где-то запоминает размер этого блока. Обычно это делается в самом блоке, "слева" от возвращенного указателя, т.е. фактически размер выделенного блока будет еще, скажем, на 4 байта больше (будем считать что наша платформа использует 4 байта для хранения размера блоков сырой памяти). Запоминать же размер массива в элементах в этом случае не надо, т.к. тип 'int' не требует деструктирования.

Выделяя же память через 'new C[8]' (где C — класс с нетривиальным деструктором) в традиционной реализации мы (с точки зрения 'operator new') просим у RTL блок памяти длиной в '8 * sizeof(C) + 4' байт. Эта подсистема даст нам такой блок (или больше) не забыв запомнить его размер в байтах "слева" от возвращенного указателя. Реализация 'new[]' же использует первые 4 байта этого блока для запоминания размера массива, т.е. запишет туда 8, сконструирует в остальной памяти 8 элементов и вернет нам указатель на первый из них. Т.е. в этом случае "слева" от возвращенного указателя хранится два размера — размер массива в элементах и размер блока памяти в байтах.

Я в своем сообщении говорил именно об опциональности хранения второго размера (размера массива в элементах). А первый размер хранится всегда. Все это Александреску никак не противоречит.
Best regards,
Андрей Тарасевич
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.