LINUX.ORG.RU

[неуч][c/c++] Об утечках памяти


0

1

Помогите неучу. Интересует ситуация такого вида:

...
char * another_function() {
  char * str = new char [10];
  ...
  return str;
}
...
void function() {
  char * var = another_function();
  ... // какой-то код. malloc, new не используются
}
...

Как-то так.

Нужно ли делать free в конце function?

нужно перестать мешать C и C++ и, в твоём случае, сделать delete [] var;

anonymous
()

Да, но вообще то так не делают. В С++ память освобождает тот, кто выделял, т.е. либо по деструктору класса, либо почитайте про smart pointer. В С я не спец, но ИМНО память вначале ф-ии выделяете, потом куда то отдаете и ее там юзают, потом в конце ф-ии освобождаете.

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

> В С я не спец, но ИМНО память вначале ф-ии выделяете, потом куда то отдаете и ее там юзают, потом в конце ф-ии освобождаете.

Если another_function() порождает объект, то она же и память под него выделяет. Такой аналог конструктора.

geekless ★★
()
char * another_function() {
  std::auto_ptr<char> str = new char [10];
  ...
  return str;
}
...
void function() {
  char * var = another_function();
  ... // какой-то код. free не нужен
}
morse ★★★★★
()
Ответ на: комментарий от geekless

ИМНО так вот и создают утечки... если в начале области видимости стоит malloc, то понятно что в конце должен чтоять free. Но когда вначале стоит некая ф-я, то ХЗ нужно free вызывать, не нужно... Я не говорю что оно рабоать не будет, но ИМНО это ужасный дизайн.

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

Да, че то лажу написал, но идея должна быть ясна.

morse ★★★★★
()

Пдобное по-моему обычно оформляется так:

T *another_create(...);
void another_delete(T*);

При использовании соблюдается правило «я тебя породил, я тебя и убью». Если делать так как ты, в вызывающем коде легко зафейлить и вместо delete[] сделать delete или free.

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

Не обязательно: есть функции, выделяющие память внутри себя и возвращающие указатель на эту область (напр., strdup). О free должна позаботиться функция, которая эту функцию вызывает, либо деструктор.

Eddy_Em ☆☆☆☆☆
()

typedef boost::shared_array<char> CharArray;

CharArray another_function ()
{
    boost::shared_array<char> str (new char[10]);
    ...
    return str;
}

void function ()
{
    CharArray var = another_function ();
    char *raw = var.get (); // будет существовать как минимум до тех пор, пока var не удалится 
}
note173 ★★★★★
()
Ответ на: комментарий от Eddy_Em

>> Не обязательно: есть функции, выделяющие память внутри себя и возвращающие

указатель на эту область (напр., strdup). О free должна позаботиться функция, которая эту функцию вызывает, либо деструктор.

Но это же моветон и ССЗБ

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

> ИМНО так вот и создают утечки... если в начале области видимости стоит malloc, то понятно что в конце должен чтоять free. Но когда вначале стоит некая ф-я, то ХЗ нужно free вызывать, не нужно... Я не говорю что оно рабоать не будет, но ИМНО это ужасный дизайн.

Неправильно ты, дядя Фёдор, бутерброд ешь.

В приплюснутом коде у тебя есть new, порождающий объект в куче, и есть delete, сносящий объект.

В Си у тебя есть производящая функция, порождающая объект в куче, и есть деинициализирующая функция, сносящая объект. Отвечать за удаление объекта обязан тот, кто вызвал производящую функцию. Инкапсуляция десу.

А вот низкоуровневые игры «я тебе указатель на память дам, а ты мне в ней что-нибудь нарисуй» как раз до добра не доводят. Если, конечно, это не взаимодействие с ядром, где иначе никак.

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

Никакой это не моветон, а очень даже удобно. Особенно если функция должна заполнить эту область памяти.

Eddy_Em ☆☆☆☆☆
()

Заодно сам спрошу: если во время выполнения блока произошло исключение, для экземпляров классов в локальных переменных вызывается деструктор?

note173 ★★★★★
()

Сделаешь delete [] когда будет надо убрать var и ненадо городить шаблоны да бусты всякие. И да, четко раздели для себя c/c++ на с и с++.

Вообще на лиспе ты бы с такой ерундой не столкнулся.

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

Если раскрутка приводит к выходу за пределы блока, то да.

geekless ★★
()

Во-первых если используешь new [], то освобождать надо через delete []. Во-вторых в C++ new это «матерное» слово и использовать его в 99.99% случаев не надо. Все проблемы с утечками решаются с помощью RAII, стандартных контейнеров и умных указателей.

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

Во-вторых в C++ new это «матерное» слово и использовать его в 99.99% случаев не надо.

кто-то путает C++ и Java?

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

Отставить панику, оно не скомпилируется:

<stdin>: В функции ‘char* another_function()’:
<stdin>:4:41: ошибка: запрошено преобразование от ‘char*’ к нескалярному типу ‘std::auto_ptr<char>’
<stdin>:6:10: ошибка: ошибка преобразования ‘std::auto_ptr<char>’ в ‘char*’ в return
legolegs ★★★★★
()
Ответ на: комментарий от shty

Нет, он всё правильно говорит. new не нужен, а за delete вообще надо сажать как за экстремизм. Ессно к разработчикам либ и тем, кому реально надо оптимизировать это не относится.

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

>>А вот низкоуровневые игры «я тебе указатель на память дам, а ты мне в ней что-нибудь нарисуй» как раз до добра не доводят.

Доводят, доводят, если руки растут откуда надо, и матчасть освоена. Как раз в примере ТС-а такую мелочь как char[10] можно создать на стеке в function и закинуть указатель в another_function. Со стек фреймом у вызывающей функции ничего не случится, пока она не завершит свою работу.
Разве что нужно быть аккуратным и не забываться, чтобы не передать этот указатель куда-нибудь налево. Но это со временем приходит.
А частое резервирование памяти на куче по мелочи приводит к ее фрагментации. И потом будете ловить совсем не очевидные баги.

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

> А частое резервирование памяти на куче по мелочи приводит к ее фрагментации. И потом будете ловить совсем не очевидные баги.

Логика-логика, где ж ты потерялась.

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

> Отставить панику, оно не скомпилируется:

это было очевидно

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

Хм... а в gtk+ часто так. Вызываешь функцию, она тебе чего-то там создает, а потом ты сам должен сделать g_free(), когда созданное больше не нужно.

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

Зачем самому возиться с выделением/освобождением памяти, а потом бороться с утечками, если есть средства языка и стандартной библиотеки, которые позволяют этого избежать?

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

есть средства языка и стандартной библиотеки, которые позволяют этого избежать

у меня есть ощущением, что я понимаю о чём идёт речь, но моск после работы совсем ватный, не поясните на примере, плиз?

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

если совсем на пальцах, то вот примеры

вот это

void func()
{
  A * a = new A ();
  /// do smth ...
  delete a;
}
хуже чем это
void func()
{
  auto_ptr < A >  a(new A ());
  /// do smth ...
}

вот это

A * get_data()
{
   return  new A[100];
}

void func()
{
 A * a = get_data();
 // do smth ...
 delete [] a;
}
хуже чем это
vector < A > get_data()
{
   return  vector < A > (100);
}

void func()
{
 vector < A  > a = get_data();
 // do smth ...
}

Reset ★★★★★
()

не нужно. нужно делать delete [] str. причём не обязательно «в конце function()»

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

Вот-вот, а я на все эти gtk насмотрелся, и сам в некоторых функциях стал выделять память и возвращать ее. Хотя, это имеет смысл делать лишь в функциях-инициализаторах, или функциях, сильно изменяющих переданную им область памяти (в т.ч. ее размер) - тогда есть смысл делать аргумент указателем на указатель...

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Reset

>В Java как раз без new никуда, а в C++ можно жить.

Это смотря в каком С++. При использовании Qt например без new никуда не деться. В общем утверждение очень спорное, понятно, что дергать вручную кучу почем зря не стоит, но и доводить идею до идиотизма тоже глупо.

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

считай, что там не new, а MegaSuperFactory::CreateObject

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

Возвращай по значению. Это не так страшно как кажется. В 99.99% случаев реально никакого копирования не случится.

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

И не спроста в стандарте кодирования гугла указано, что результат функций возвращаются ТОЛЬКО по значению через return, а не через аргументы.

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

И еще там есть автоматическое убийство детей родителями, что все равно не освобождает от необходимости вызывать new и иногда delete (например для уже не нужных виджетов).

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

А я у себя в некоторых местах, где нужно создать относительно большой массив байт, который потом может утечь куда угодно, использовал boost::shared_ptr<std::vector>. Это не очень плохо?

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

Выглядит подозрительно. Смотреть конкретную ситуацию нужно. Мне обычно удавалось избегать подобных ситуаций.

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

Данные — распакованное изображение. Используется разными объектами при рисовании, объекты могут копироваться, храниться, перемещаться, пока кому-то нужна текстура — она должна быть в памяти.

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

Ну да, конец поста в спешке дописывал, логика пропала. Как раз вспомнилось, что на фрагментированной памяти malloc в какой-то из старых MSVC начинал дико себя вести. Не известно, почему это проявлялось, но тем не менее крови попортило.

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