LINUX.ORG.RU

delete[] C++


0

0

void fun(Object *abc)
{
  delete[] abc;
}

Что будет? Он ведь не знает размера массива, на который указывает abc.
Нужно, чтобы для каждого элемента корректно вызвался деструктор.
anonymous

А вы знаете толк в извращениях на C++.

wfrr ★★☆
()

> Он ведь не знает размера массива, на который указывает abc

Правда? А ты пробовал это в реальности, или чисто потроллить?

tailgunner ★★★★★
()
Ответ на: комментарий от imp

> А как free узнает сколько освобождать? Наверно то же самое с массивами...

glibc знает, по какому адресу сколько памяти выделено. К тому же, free() никаких деструкторов не вызывает.

mv ★★★★★
()
Ответ на: комментарий от mv

Про free я знаю. Но тут не просто память освобождать, тут - деструкторы.

anonymous
()
Ответ на: комментарий от anonymous

> А где он его хранит-то?

Точно не знаю.

> Когда массив объявлен явно, то компилятору это видно, а тут?

А тут он указывается при создании массива:

p = new Object[alen];

tailgunner ★★★★★
()

> Он ведь не знает размера массива, на который указывает abc.

C++ аллокатор обязан хранить размер массива. Поэтому он знает сколько деструкторов вызвать.

dilmah ★★★★★
()
Ответ на: комментарий от dilmah

Ладно. Предположем мне нужно удалить только хвост массива (начиная с 6ого элемента):

#include <iostream>

using namespace std;

int i = 0;

class object { int a; public: object() { a = i++; } ~object() { cout << "Deleted " << a << "\n"; } };

int main() { int n; cout << "Enter n: "; cin >> n; object *a = new object[n]; delete[] (a+5); return 0; };

Вывод неутешительный:

Enter n: 11 Deleted 8 Deleted 7 Deleted 6 Deleted 5

Удалил середину!

anonymous
()
Ответ на: комментарий от dilmah

Ладно. Предположем мне нужно удалить только хвост массива (начиная с 6ого элемента):

#include <iostream>

using namespace std;

int i = 0;

class object
{
  int a;
public:
  object()
  {
    a = i++;
  }
  ~object()
  {
    cout << "Deleted " << a << "\n";
  }
};

int main()
{
  int n;
  cout << "Enter n: ";
  cin  >> n;
  object *a = new object[n];
  delete[] (a+5);
  return 0;
};

Вывод неутешительный:

Enter n: 11
Deleted 8
Deleted 7
Deleted 6
Deleted 5

Удалил середину!

anonymous
()
Ответ на: комментарий от xTERM

> Знаю. Но С++ претендовал на звание языка высокого уровня ;)

C++ претендует на звание многуровневого мультипарадигмного мульти-языка. Хотите высокого уровня, не используйте C-массивы.

anonymous
()
Ответ на: комментарий от xTERM

> Но С++ претендовал на звание языка высокого уровня ;)

И в этой претензии был пункт "массивы Си++ будут несовместимы с массивами Си"? Не припомню. Впрочем, я и самой претензии не припомню.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> И в этой претензии был пункт "массивы Си++ будут несовместимы с массивами Си"? Не припомню. Впрочем, я и самой претензии не припомню.

Проснутся пора уже, из анабиоза, C уже давно не является подмножеством C++, так что никто не обязан ни с кем быть совместимым, это раз.

А два: они собственно совместимы, в чем несовместимость то? Если вы про stl, то и тут вы не в тему, std::vector бинарно совместим с сишными массивами, берите &vec[0], и подсовывате туда, куда раньше адрес массива передавали, проблем не возникнет.

fmj
()
Ответ на: комментарий от mv

>Кастую в топик Absurd'а высшего уровня

Нафиг надо растрачивать силы? Топег про GTK в топе, на С++ срем там. Приглашаются плюсофилы в качестве жертв.

Absurd ★★★
()
Ответ на: комментарий от fmj

> C уже давно не является подмножеством C++

Смотря какой Си.

> так что никто не обязан ни с кем быть совместимым, это раз.

Обязан. Мне лично обязан, устроит?

> А два: они собственно совместимы, в чем несовместимость то?

И где я сказал, что они несовместимы?

> std::vector бинарно совместим

Посмотри в заголовок - не о std::vector речь.

tailgunner ★★★★★
()

... Речь идет не о строении компьютерного языка, а скорее о нашем собственном строении. Все уродства С++ — это в основном наши уродства. Когда вы научитесь понимать и любить его странности, когда перестанете беспокоиться о математической стройности, будет сделан ваш первый шаг к достижению элегантности в С++. (c) Джефф Элджер

fmj
()
Ответ на: комментарий от tailgunner

Допустим, но речь о том, что неправославно использовать Object[] когда есть vector<Object>.

Ну и конечно, ответ топикстартеру: удалит все, мемори-манагер знает сколько аллокировано элементов в массиве, а если хочется иметь возможность получить размер массива, -- юзойте stl

fmj
()
Ответ на: комментарий от fmj

>Когда вы научитесь понимать и любить его странности, когда перестанете беспокоиться о математической стройности, будет сделан ваш первый шаг к достижению элегантности в С++.

Похрен на элегантность, надо чтобы работало. В первую очередь корректно, во вторую - быстро. И чтобы мэйтенанс кода был хороший. В этот топег я больше не пишу - влом следить.

Absurd ★★★
()
Ответ на: комментарий от fmj

>Все уродства С++ — это в основном наши уродства.

Если почитать "Дизайн и эволюцию С++" от Страуструпа, то понимаешь, что у этого языка нету будущего. Проблем слишком много, чтобы не обращать на них внимания.

anonymous
()
Ответ на: комментарий от anonymous

На том же WinAPI можно страться писать красиво, но в лучшем случае получится не слишком страшно. Так и тут.

anonymous
()

> Он ведь не знает размера массива, на который указывает abc.

Страуструп вроде писал, что размер массива в таком случае определяется по информации от распределителя памяти.

А удалять середину не надо даже пытаться. Память освобождается только теми же кусками, какими выдавалась, и никак иначе. Я вообще удивлён, что при "удалении середины" кора не выпала.

alexsaa
()
Ответ на: комментарий от anonymous

>Хотите высокого уровня, не используйте C-массивы.

Не вижу противоречия. Если new[]/delete[] хранят размер массива, то можно было бы ввести ключевое слово для его изъятия (не sizeof, ибо это размер указателя). Вполне хороший компромисс. Вместо того, чтобы стандартизовывать всякие boost'ы, надо было сначала подумать об основополагающий вещах. Или дело в какой-то особой индийской религии?

xTERM ★★
()
Ответ на: комментарий от xTERM

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

> не sizeof, ибо это размер указателя

не sizeof ибо sizeof это константное выражение известное на этапе компиляции

dilmah ★★★★★
()
Ответ на: комментарий от alexsaa

>Страуструп вроде писал, что размер массива в таком случае определяется по информации от распределителя памяти

а в стандарте написано, что new/delete могут быть реализованы посредством malloc/free, а могут и не быть. а ещё сами malloc/free можно очень по-разному реализовать, и очень разную информацию при выделении блока памяти в нагрузку к указателю записывать

jtootf ★★★★★
()
Ответ на: комментарий от xTERM

>Если new[]/delete[] хранят размер массива

не хранят

jtootf ★★★★★
()
Ответ на: комментарий от xTERM

>Вместо того, чтобы стандартизовывать всякие boost'ы

вместо того, чтобы задавать глупые вопросы, можно думать. рекомендую

>Или дело в какой-то особой индийской религии?

дело в интерфейсе работы с памятью языка C и перегруженной (в смысле - несущей слишком много функций) в C++ семантике указателя. альтернативный вариант - регионы Cyclone

jtootf ★★★★★
()
Ответ на: комментарий от mv

>Он (list ...) использует

вот взгляд политически верный и расовый (с)

jtootf ★★★★★
()
Ответ на: комментарий от dilmah

> undefined behaviour..

Очень даже defined. glibc ядру освобождённую память сразу же не вернёт (тем более, она наверняка посередине странички освобождена), GPF не будет.

mv ★★★★★
()
Ответ на: комментарий от dilmah

> это undefined behaviour

Но не segmentation fault. Если sigsegv прилетел, то это баг в glibc, когда он по дереву ходил.

mv ★★★★★
()

>> Что будет? Он ведь не знает размера массива, на который указывает abc. >> Нужно, чтобы для каждого элемента корректно вызвался деструктор.

Существует несколько способов сохранить размер массива при вызове new[], так что delete[] знает сколько объектов в массиве и корректно вызовет для них деструкторы.

По вопросу касательно вызова delete на отдельном объекте из массива: даже если это и работает на протых тестах, это грязный хак. Вообще, delete делает две вещи: 1) вызывает деструктор и 2) освобождает память. Если нужно только вызвать деструктор для одного объекта - вызывайте напрямую, но не забудьте что _стандартный_ delete[] на всём моассиве позже его вызовет снова. Освободить память только одного элемента из массива в общем случае невозможно, так что если вам зачем-то нужно подобное - используйте связанные списки.

Deleted
()
Ответ на: комментарий от mv

> Но не segmentation fault. Если sigsegv прилетел, то это баг в glibc, когда он по дереву ходил.

Иш ты?

Обьяснение для IQ > 90: менеджер памяти хранит служебную информацию о блоках памяти обычно в служебном мини-блоке, перед самим блоком, обычно это 16 байт (на 32х битных машинах), содержащие размер распределенного блока памяти, а так-же указатели на кучу, и на следующий/предыдущий элементы. Таким образом, при вызове free(ptr), функция free вычисляет смещение ptr-16 байт, и по этому адресу пытается найти служебную информацию, относящуюся к распределению памяти в куче. Если мы сделаем free(ptr+1), очевидно, это приведет к тому, что glibc прочитает совершенно некорректные данные, и попытается по некорректным адресам, из этих некорректных данных, обновить значения, хранимые в heap-е. Это очевидно, с некоторой вероятностью, закрешит приложение. Однако, если это сразу закрешит -- это БОЛЬШОЕ ВЕЗЕНИЕ. ПРОСТО ОГРОМНОЕ. В большинстве случаев, подобные детские ляпы приводят к тому, что приложение начинает крешится только некоторое время спустя, после некорректного вызова. Отлаживать такие ошибки -- просто пестетц (хотя спасают такие прелести как libgmalloc и аналоги в других системах).

Вариант обьяснения для IQ <= 90: Предположим я malloc-ом выделил 4096 байт, а malloc у меня такой, каждый регион выделенный (крупнее 16 байт) окружает с 2х сторон страницами, доступ к которым выкидывает TRAP аппаратный, и по free зануляет всю освобождаемую память. Может такое быть? еще как, пример реализации: дебажная версия malloc-а в OS X (при использовании libgmalloc-а). При вызове free(a+1) мы одним байтом залезем на страницу-ловушку, в результате все приложение рухнет. Отметим тот факт, что такая реализация malloc/free никак не противоречит ни одному из стандартов.

fmj
()
Ответ на: комментарий от dilmah

> a = malloc(...);

> free(a+1);

> это undefined behaviour

Вообще-то это

xxxxxxxx.cpp: In function `blah-blah':

xxxxxxxx.cpp:N: error: invalid conversion from `void*' to `YOUR_TYPE*'

fmj
()
Ответ на: комментарий от fmj

> error: invalid conversion from `void*' to `YOUR_TYPE*

очень содержательное замечание. Особенно учитывая то что Си отличается от Си++ в этом отношении -- там это не ошибка.

dilmah ★★★★★
()
Ответ на: комментарий от fmj

> Таким образом, при вызове free(ptr), функция free вычисляет смещение ptr-16 байт, и по этому адресу пытается найти служебную информацию, относящуюся к распределению памяти в куче. Если мы сделаем free(ptr+1),

Сам придумал?

> Вариант обьяснения для IQ <= 90: Предположим я malloc-ом выделил 4096 байт, а malloc у меня такой, каждый регион выделенный (крупнее 16 байт) окружает с 2х сторон страницами, доступ к которым выкидывает TRAP аппаратный, и по free зануляет всю освобождаемую память. Может такое быть?

Нет. Наименьшая гранулярность, которой выделяется память (добавляется новый vma в адресное пространство процесса) - страница памяти (обычно 4кб).

mv ★★★★★
()
Ответ на: комментарий от mv

>> Таким образом, при вызове free(ptr), функция free вычисляет смещение ptr-16 байт, и по этому адресу пытается найти служебную информацию, относящуюся к распределению памяти в куче. Если мы сделаем free(ptr+1),

> Сам придумал?

А что, есть возражения?

> Наименьшая гранулярность, которой выделяется память (добавляется новый vma в адресное пространство процесса) - страница памяти (обычно 4кб).

Н-да...

Ты не прав. Ты путаешь выделение памяти _системой_ процессу, и выделение памяти из кучи ( куча, строго говоря, _вся_ принадлежит процессу и (в современных системах) лишь расширяется (подобно стеку) по мере необходимости ).

Die-Hard ★★★★★
()
Ответ на: комментарий от mv

Не будем смущать одминофф.

> Сам придумал?

*******, хватит троллить, возьми в зубы компилер, 
тестовый пример, и посмотри что получится.

Мне удалось стабильно крешить вот таким кодом на 32х битной системе:

// хедеры, инт мейны, все пропущено, для читабельности
for (;;)
{
	int *ptr = malloc(1024);
	// Вполне законное заполнение массива каким-то данными
	ptr[0] = ptr-1024;	
	ptr[1] = ptr-1024;
	ptr[2] = ptr-1024;
	ptr[3] = ptr-1024;
	// семантика ptr+1 зависит от типа указателя, так что если 
	// ptr есть понтер на 16-байтную структуру, то 
	// нижеприведенный вызов будет эквивалентен исходному free(ptr+1);
	free(ptr+4); 	
} 

Про твой ответ, с дебажным выводм glibc-а про double free: 
glibc-ы разные бывают.

Собирал MinGW-шным и CygWin-овским gcc, в обоих случаях код просто рушится. 
В сузятине 10.x выдает дебажный мат, однако я не вижу большой разницы между SIGSEGV-ом 
и SIGABRT-ом (выкинутым благодаря вызову abort изнутрей glibc), и то и то приложение обязано
обрабатывать терменированием самого себя.


> Нет. Наименьшая гранулярность, которой выделяется память (добавляется новый vma в адресное пространство процесса) - страница памяти (обычно 4кб).

*******, хватит троллить, тебе ясным языком было написано: 
"я malloc-ом выделил 4096 байт".

fmj
()

Ответ анонимусу с IRIX'ом. По поводу достаточности факта успешного завершения компиляции: нет, не считаю, что это достаточно. glibc хранит информацию о выделенной памяти не в перемежку с самой выделенной памятью (загляните в исходники, там, если память не изменяет, красно-чёрное дерево). На IRIX'е оно, конечно, может быть под другому, я IRIX не видел. Если fmj хотел уронить процесс по sigsegv, то можно было освободить заведомо плохой для данного примера пойнтер, например, 4096.

mv ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.