Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Работа оператора delete[]


Автор: Gelos 1.5.2007, 14:10
Прочитал темы посвещенные данному оператору, но то что интересует не нашел.  

В случае, если я просто вызываю оператор delete [] mass;  то соотвественно , все что было в этой памяти уничтожается, но что происходит с памятью? У меня есть подозрение, что освобожденная память не возвращается обратно в систему. А дальше по циклу, я снова выделяю память, по этому же указателю, и так пока программа не съедает все виртуальную память на машине, и благополучно умирает. Сегодня наткнулся на запись в книге, что, для того чтобы освобожденная память была возвращена в систему, оператор delete помещается в деструктор.  Так ли это?  или в любом случае , освобожденная память возвращается в систему?

Автор: sergejzr 1.5.2007, 14:16
"Возвращается" конечно. Освобождённые блоки помечаются свободными и будут использованы при первом удобном случае.
А с чем связано подозрение?

Автор: Xenon 1.5.2007, 14:16
Память выделенную с new T удаяешь при помощи delete pTr, а new T[] удаляешь delete [] pTr. Если попытаешься стереть то, что выделил с new [] простым delete, то получишь UB. 
И что значит не возвращаться в память? При вызове delete вызывается деструктор, а потом free, который освобождает память.

Автор: Gelos 1.5.2007, 14:33
Подозрение связано с тем, что очень хорошо в диспетчере задач видно , как программа выедает память. При отладке в дебагере видно, что данные уничтожаются. И тем не менее, память исчезает. Поэтому и возникла мысль, что освобожденная память так и остается освобожденной областью памяти, в которой ничего нет, а на следующей итерации цикла, где снова вызывается оператор new память опять выделяется, и данные записываются в неё, а не в ту ранее освобожденную область .

Автор: Romikgy 1.5.2007, 14:37
имхо где то есть участок кода где есть выделение памяти , но нет освобождения

Автор: Gelos 1.5.2007, 15:00
Romikgy,
ладно... придется дальше  искать.

Значит, при операторе delete память "возвращается   в общий кусок" а не остается висеть в пространстве пустой областью просто занимающей место. Так?

Автор: Romikgy 1.5.2007, 15:43
Цитата(Gelos @  1.5.2007,  14:00 Найти цитируемый пост)
Так?

так

Автор: archimed7592 1.5.2007, 16:30
Цитата(Gelos @  1.5.2007,  14:10 Найти цитируемый пост)
Сегодня наткнулся на запись в книге, что, для того чтобы освобожденная память была возвращена в систему, оператор delete помещается в деструктор.
вот что имелось в виду
Код
class A
{
    int *p;
public:
    A ()
        : p (new int [100])
    {
    }
    ~A ()
    {
        delete [] p; // если эту строку закоментировать, то будут утечки
    }
};

int main ()
{
    while (true)
    {
        A *pa = new A [100];
        delete [] pa;
    }
    return 0;
}

Автор: Rockie 1.5.2007, 18:31
Цитата(Gelos @  1.5.2007,  15:00 Найти цитируемый пост)
ладно... придется дальше  искать.Значит, при операторе delete память "возвращается   в общий кусок" а не остается висеть в пространстве пустой областью просто занимающей место. Так?

Gelos, покажи код, а то так ты можешь еще очень долго выяснять 



Автор: Mayk 1.5.2007, 18:46
Цитата(Gelos @  1.5.2007,  18:10 Найти цитируемый пост)
А дальше по циклу, я снова выделяю память, по этому же указателю, и так пока программа не съедает все виртуальную память на машине, и благополучно умирае

Найди какой-нибудь инструмент, отлавливающий memory-leak'и.

Или свой напиши --- перегружаешь операторы new и delete, при вызове new записываешь куда-либо что было выделение. При вызове delete'ов затираешь что было выделение. В конце работы выводишь какие участки памяти не были освобождены. 


Автор: Gelos 1.5.2007, 19:32
Rockie
Код проекта уже весьма большой, поэтому приводить не имеет смысла. 


Mayk
Спасибо, возьму на заметку. хорошая идея..

Автор: Rockie 1.5.2007, 19:50
а, блин smile)

Цитата(Gelos @  1.5.2007,  14:10 Найти цитируемый пост)
 А дальше по циклу, я снова выделяю память, по этому же указателю

Семен Семеныч.. smile

Добавлено через 4 минуты и 4 секунды
Gelos, ты выделяешь память в цикле, но навряд ли там же освобождаешь. В итоге память выделяется, не удаляется и снова выделяется. Поэтому программа жрет память, что и должна делать smile new и delete должны вместе находиться в цикле.




Автор: Gelos 1.5.2007, 20:18
Rockie
ну щаз ) как же. в обоих циклах, как в главном так и вложенном память выделяется и там же уничтожается.  smile 

Автор: Rockie 1.5.2007, 21:04
Цитата(Gelos @  1.5.2007,  20:18 Найти цитируемый пост)
в обоих циклах, как в главном так и вложенном память выделяется и там же уничтожается.  smile   


Gelos, чего ты жадничаешь? покажи функцию с этими циклами))


Автор: Gelos 1.5.2007, 22:13
Rockie
кхе....
Да дело не в жадности. Просто весь проект взаимосвязан. Я не могу от туда взять кусок, и и показать. Мол тут  непонятно. А  выкладывать весь код, я редактировать устану. 

Автор: Xenon 1.5.2007, 22:17
Тогда и мы вряд ли чем поможем smile

Автор: Mayk 1.5.2007, 22:52
Цитата(Gelos @  1.5.2007,  18:10 Найти цитируемый пост)

В случае, если я просто вызываю оператор delete [] mass; 

а обязательно использовать эти ненадёжные new[]/delete[]'ы? 
контейнеры и smart pointerы не пойдут?

Цитата(Gelos @  2.5.2007,  02:13 Найти цитируемый пост)

Да дело не в жадности. Просто весь проект взаимосвязан. Я не могу от туда взять кусок, и и показать. Мол тут  непонятно. А  выкладывать весь код, я редактировать устану. 

Кстати. А утечка памяти происходит именно в этих циклах? 
А не в других ф-циях, вызываемых из этих циклов?

Автор: Gelos 1.5.2007, 23:01
Mayk

Функция одна. она же главная. Я не стал заводить множество функций под каждую операцию в проекте, хоть и признаю, что самым лучшим тоном было бы создать одну функцию, которую бы вызывал с разными параметрами. Ну да не будем переходить к специфике проекта самого.  
И... Как узнаю и научусь пользоваться контейнерами и smart pointerами  то обязательно сравню чем лучше. На данный момент, это для меня неизвестные термины. Все ещё впереди.

Xenon
Уже помогли. Причем достаточно сильно. Спасибо.

Автор: Vyacheslav 2.5.2007, 13:37
Цитата(Mayk @  1.5.2007,  22:52 Найти цитируемый пост)

а обязательно использовать эти ненадёжные new[]/delete[]'ы? 
контейнеры и smart pointerы не пойдут?

А где в стандарте описывается вероятность, с которой должен "ненадёжный" delete[] должен освобожать память выделенную "ненадёжным" new[]  smile 
И чем, простите, пользуются Ваши волшебные "контейнеры и smart pointerы", что бы  избежать этой ненадежности  smile    ?

Автор: Anikmar 2.5.2007, 14:18
Цитата(Vyacheslav @  2.5.2007,  13:37 Найти цитируемый пост)
А где в стандарте описывается вероятность, с которой должен "ненадёжный" delete[] должен освобожать память выделенную "ненадёжным" new[]   
И чем, простите, пользуются Ваши волшебные "контейнеры и smart pointerы", что бы  избежать этой ненадежности      ?


Пожалуй слово "ненадежный" надо употреблять к программеру.

Я так понял суть вопроса была в нехватке памяти. Где-то с год назад в другом формуе была похожая проблема. Весь вопрос был в том, что в процессе работы выделялось огромное количество небольших кусочков, потом освобождалось, потом снова выделялось. Я написал небольшой тест (сейчас найти его не смог). Программа только и делала, что выделяла и освобождала небольшие участки памяти. Стабильный вылет по нехватке памяти примерно через 10 минут работы. Естественно все корректно освобождалось - там ошибиться было негде.

Тогда все на форуме пришли к дружному мнению, что менеджер памяти в Борланде оказался не готов к таким вещам. Но чтобы это достичь, понадобилось действительно очень много раз выделять и освобождать много кусочков. А вот куда тестовый код дел - Бог его знает, видимо затер...

Автор: Mayk 2.5.2007, 18:28
Цитата(Vyacheslav @  2.5.2007,  17:37 Найти цитируемый пост)
smile 
И чем, простите, пользуются Ваши волшебные "контейнеры и smart pointerы", что бы  избежать этой ненадежности 

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

Ненадежность пары new/delete заключается в том, что их надо писать руками. А если не напишешь --- то получишь memory leak. 

Контейнеры и смарт поинтеры умеют пытаются чистить память самостоятельно.  Зачастую довольно успешно.
Или я что-то пропустил и теперь без  явного my_vector.clear() память контейнеры уже не освобождают? user posted image

Цитата(Vyacheslav @  2.5.2007,  17:37 Найти цитируемый пост)

А где в стандарте описывается вероятность, с которой должен "ненадёжный" delete[] должен освобожать память выделенную "ненадёжным" new[]

А Вы видели код автора темы? Откуда такая уверенность что на каждый new[] имеется delete[]?

Автор: Ken 2.5.2007, 19:10
Если память выделяется и освобождается внутри одной функции и еще размер массивов не очень большой то можете выделить ее в стеке. Это работает очень быстро и автоматом очищается. Например:
Код

void processingFunc (char* buffer, int size)
{
...
    // Допустим здесь надо выделить size * 10 байт памяти
    char tempBuffer [size * 10];
...
    // Дальше используем tempBuffer как хотим и при выходе из функции (даже из блока)
    // она автоматом освобождается.
}


Оператор new и delete не очень легкие операции. При частом использовании в некоторых случаях может провести к фрагментации адресного пространства процесса. Но этого (многократного создания и удаления) можно избежать, например таким образом:
Код

static char* tempBuffer = 0;
static int tempBufferSize = 0;

void processingFunc (char* buffer, int size)
{
    // Допустим здесь надо выделить size * 10 байт памяти
    int len = size * 10;

    if (len > tempBufferSize)
    {
        tempBufferSize = len;
        delete [] tempBuffer;
        tempBuffer = new char [tempBufferSize]
    }

    // Дальше используем tempBuffer как хотим,
    // освобождаем в конце работы программы
}


Автор: Earnest 2.5.2007, 19:11
А не ищете ли вы в темной комнате черную кошку которой там нет Откуда уверенность, что есть утечки? Менеджер процессов показывает только возрастание? Так он за такими мелочами, как отдельные кусочки в хипе не следит, хотя бы потому, что управление хипом может по разному происходить в разных программах. Кроме утечек есть ведь такая вещь, как фрагментация памяти... Т.е. выделенный объем хипа большой, а память в нем нарезана мелкими дольками: и свободной вроде много, и непрерывный кусок не найти...

Автор: Vyacheslav 2.5.2007, 19:54
Цитата(Mayk @  2.5.2007,  18:28 Найти цитируемый пост)
Извиняюсь за такую банальность --- деструкторами( которым обычные указатели, кстати, не могут похвастать).

Ничего страшного, я Вас извиняю. Но продолжим дальше. А чем пользуются деструкторы, что осободить память? delete[] не предлагать в виду его "ненадежности" smile

Цитата(Mayk @  2.5.2007,  18:28 Найти цитируемый пост)
Ненадежность пары new/delete заключается в том, что их надо писать руками. А если не напишешь --- то получишь 

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

Цитата(Mayk @  2.5.2007,  18:28 Найти цитируемый пост)
Контейнеры и смарт поинтеры умеют пытаются чистить память самостоятельно.  Зачастую довольно успешно.
Или я что-то пропустил и теперь без  явного my_vector.clear() память контейнеры уже не освобождают? 


"Зачастую довольно успешно." Как ? И они тоже проявляют ненадежность?

Цитата(Mayk @  2.5.2007,  18:28 Найти цитируемый пост)
А Вы видели код автора темы? Откуда такая уверенность что на каждый new[] имеется delete[]?

Ну Вы тоже его не видели. Но даже если это и так, то я  считаю, что неправильно лечить кривизну рук применением   приемов и идиом программирования.  Ни на квалификации, ни на качестве кода это в конечном счете не скажется. Код останется таким же сомнительным.
Что же касается темы топика, то выскажу мысль о том, что судить об утечках основываясь на показаниях  менеджера процессов,  не кажется мне достаточно разумным. 

Автор: Mayk 2.5.2007, 20:47
Цитата(Vyacheslav @  2.5.2007,  23:54 Найти цитируемый пост)

Ничего страшного, я Вас извиняю. Но продолжим дальше. А чем пользуются деструкторы, что осободить память? delete[] не предлагать в виду его "ненадежности"

я ж уже написал что
Цитата(Mayk @  2.5.2007,  22:28 Найти цитируемый пост)

Ненадежность пары new/delete заключается в том, что их надо писать руками

Забыть вызвать деструкторы весьма проблематично. 
Забыть вызвать delete[]/free()/fclose/pthread_mutex_unlock/etc достаточно легко. Особенно в громоздкой ф-ции.

Цитата(Vyacheslav @  2.5.2007,  23:54 Найти цитируемый пост)

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

В неудобстве использования. 
Или Вы в самом деле считаете, что явный вызов delete[]  является  верхом удобства? 

Цитата(Vyacheslav @  2.5.2007,  23:54 Найти цитируемый пост)

"Зачастую довольно успешно." Как ? И они тоже проявляют ненадежность?

Поиск по ключевым словам "циклическая зависимость"(это о не абсолютной надежности смарт поинтеров)  и 
обдумывание vector<int*> mv(1, new int[100]); return; (это о не абсолютной надежности при использовании контейнеров)

Цитата(Vyacheslav @  2.5.2007,  23:54 Найти цитируемый пост)

Ну Вы тоже его не видели. Но даже если это и так, то я  считаю, что неправильно лечить кривизну рук применением   приемов и идиом программирования.  Ни на квалификации, ни на качестве кода это в конечном счете не скажется. Код останется таким же сомнительным.

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

Цитата(Vyacheslav @  2.5.2007,  23:54 Найти цитируемый пост)

Что же касается темы топика, то выскажу мысль о том, что судить об утечках основываясь на показаниях  менеджера процессов,  не кажется мне достаточно разумным.  

К сожалению других данных у нас нет. Но лишний прогон через детекторы memory leak'ов хуже не сделает. 

Автор: Rockie 2.5.2007, 21:14
Судя по
Цитата(Gelos @  1.5.2007,  14:10 Найти цитируемый пост)
 А дальше по циклу, я снова выделяю память, по этому же указателю, и так пока программа не съедает все виртуальную память на машине, и благополучно умирает.

Цитата(Gelos @  1.5.2007,  20:18 Найти цитируемый пост)
ну щаз ) как же. в обоих циклах, как в главном так и вложенном память выделяется и там же уничтожается.  smile  

возможно автар создал нечто вроде 

Код

    for(...)
    {
        int* p = new int[100];     // выделили

        for(...)
        {
            p = new int[100]; // еще выделили(старая еще торчит в памяти?) 
            delete [] p;
        }
        delete [] p;                // какой-нить UB
    }



хотя как оно было на самом деле мы уже вряд ли узнаем))


Автор: vinter 2.5.2007, 21:16
Цитата(Mayk @  2.5.2007,  20:47 Найти цитируемый пост)
Забыть вызвать delete[]/free()/fclose/pthread_mutex_unlock/etc достаточно легко. Особенно в громоздкой ф-ции.

это проблемы программиста, он не должен забывать. 

Автор: Vyacheslav 2.5.2007, 21:16
Цитата(Mayk @  2.5.2007,  20:47 Найти цитируемый пост)
обдумывание vector<int*> mv(1, new int[100]); return; (это о не абсолютной надежности при использовании контейнеров

Вы знаете, Я в полной растеренности. Вы действительно уверены, что  vector предназначен для  только для того, чтобы уберечь неокрепшие души  начинающих программистов от написание ужасного delete? 
Потому как в этом коде, 
Код

vector<int*> mv(1, new int[100]); return

я вижу , как создается вектор указателей на инт размером 1  и тут же инициализируется адресом на массив, выделенный оператором new[]. Насколько я понимаю, здесь мы имеем возможность наблюдать  ту же кривизну рук. Почему Вы  это отнесли этот код к " не абсолютной надежности при использовании контейнеров" для меня загадка

Автор: Mayk 2.5.2007, 21:53
Цитата(vinter @  3.5.2007,  01:16 Найти цитируемый пост)
Цитата(Mayk @  2.5.2007,  20:47 Найти цитируемый пост)
Забыть вызвать delete[]/free()/fclose/pthread_mutex_unlock/etc достаточно легко. Особенно в громоздкой ф-ции.
это проблемы программиста, он не должен забывать.  

угу, только удобства это не добавляет. RAII же эту проблему решает достаточно элегантно. 
я уже не помню когда в последний раз писал close() для закрытия файлов. 


Цитата(Vyacheslav @  3.5.2007,  01:16 Найти цитируемый пост)
Вы действительно уверены, что  vector предназначен для  только для того чтобы уберечь неокрепшие души  начинающих программистов от написание ужасного delete

Предоставьте ссылку на пост где я писал что он предназначен только для этого. 
 
Цитата(Vyacheslav @  3.5.2007,  01:16 Найти цитируемый пост)
Почему Вы  это отнесли этот код к " не абсолютной надежности при использовании контейнеров" для меня загадка

По определению
Цитата(http://dic.gramota.ru/search.php?word=%E0%E1%F1%EE%EB%FE%F2%ED%FB%E9&lop=x&gorb=x&efr=x&zar=x&ag=x&ab=x&lv=x&pe=x&az=x)

АБСОЛЮТНЫЙ прил.
2. Достигший высшего предела; полный, совершенный.

Наличие memory leak'ов свидетельствует о том, что код не является соврешенным, 
следовательно он не является абсолютно надёжным в плане памяти несмотря  несмотря на использование контейнера.
Напомню что это было к реплике
Цитата(Vyacheslav @  2.5.2007,  23:54 Найти цитируемый пост)

"Зачастую довольно успешно." Как ? И они тоже проявляют ненадежность?

Вот подобный пример как раз показывает когда stl'овские контейнеры проявляют ненадежность.

Для примера --- QPtrCollection из QT(это базовый тип для коллекций указателей) имеет свойство autoDelete, при установке которого элементы данного контейнера будут delete'нуты. (знаю-знаю, delete отличается от delete[] --- суть не в этом, суть в том что иногда контейнер вполне успешно может контролировать жить элементам, или нет).

Автор: Earnest 3.5.2007, 07:17
Mayk
Vyacheslav,
я как-то упустила нить вашей дискуссии... Да и не только я, полагаю... smile
Вы оба правы: хорошему программисту STL контейнеры, RAII и прочая, конечно, не помешают, но плохому - не помогут...
Стоит только посмотреть (да хотя бы по этому форуму), ЧТО народ пытается в контейнеры засунуть... smile   
Кривые руки одним использованием STL не выпрямляются... Даже наоборот, появляется искусс и дальше ничего не понимать... Пороть надо, наверное... smile 

Автор: Vyacheslav 3.5.2007, 12:09
Earnest,  извините. Наверное Ваше сообщение призвано прикратить офтоп, но я рискну продолжить. 
Здесь уже вопрос затронут не о применении STL, а о том что  уважаемый Mayk пытается неумение написать код списать на якобы имеющиеся недостатки языковых конструкций.  С моей точки зрения это просто возмутительно smile Похоже все эти примочки произвели на него неизгладимое впечатление, что он уверен, что  применение их способно выправить кривизну рук 
Итак , уважаемый Mayk, Вы можете пояснить в чем ненадежность данного кода?
Код

void foo() throw (std::exception)
{
char *pchar = new [1000];
// далее следует небезопасный код
// который может сгенерить std::exception
try{
//....

}
catch( std::exception& exp )
{
  delete [] pchar;
  // обработка  ошибок  по дизайну происходит не здесь
  throw;

delete [] pchar;
}
 


Теперь и немного перепишем его, например , воспользуясь auto_ptr. Хотя  нет, auto_ptr в данном случае не совсем уместен. Поэтому мы воспользуемся своим. Возможно  класса этот немного некорректен, но для демонстрации сойдет
Код

// auto_ptr не пойдет
// 
template<class _Tp>
struct auto_aptr : public std::auto_ptr< _Tp>
{
   explicit auto_aptr( _Tp* __px  ):std::auto_ptr<_Tp>(__px) {}
   ~auto_aptr()  { reset();  }
    void reset(_Tp* __px=0) 
   { 
             _Tp* __pt = get();
             if (__px != __pt) 
                    delete[] __pt; 
             __set(__px); 
   }
} ;

void foo() throw (std::exception)
{
  auto_aptr<char > pchar(new  MyClass[1000]);
// далее следует небезопасный код
// который может сгенерить std::exception

//...

}
 
Уверяю Вас, что и в первом и во втором случае  код "абсолютно" надежен, несмотря на то что в том и другом случае  используются new[] и delete[], написанные ручками , но я, пожалуй,  соглашусь, что во втором случае он оказался немного "совершенней" что ли. И если  и тот и другой  мне попадут на  code review я приемлю с удовлетворением и ту и другую версию. Единственное, что я не приемлю, так это наличие "несовершенного кода" с наличем memory leak'ов. И дело тут не в совершенстве, или несовершестве, а банальной ошибке. 

Цитата(Mayk @  2.5.2007,  21:53 Найти цитируемый пост)
Наличие memory leak'ов свидетельствует о том, что код не является соврешенным, 

Наличие memory leak'ов  свидельствует не о том, что код не является совершенным, а том что он написан с ошибками smile .  
И будьте уверены, программисту на это будет указано. 

Цитата(Mayk @  2.5.2007,  21:53 Найти цитируемый пост)

Для примера --- QPtrCollection из QT(это базовый тип для коллекций указателей) имеет свойство autoDelete, при установке которого элементы данного контейнера будут delete'нуты. (знаю-знаю, delete отличается от delete[] --- суть не в этом, суть в том что иногда контейнер вполне успешно может контролировать жить элементам, или нет).
 

Ну знаете, а чего же нарываетесь  smile  ? Я не знаком с QPtrCollection, но предполагаю, что в случае, если я захочу воспользоваться этой коллекцией, для того чтобы держать указатели на массивы объектов , выделенные с помощью new[],  мне потребуется переопределить какую нибудь виртуальную функцию типа clear. А если  я этого опять же из-за кривизны рук не сделаю, то получу на этот раз абсолютный  (см АБСОЛЮТНЫЙ прил. 2. Достигший высшего предела; полный, совершенный. ) неработающий код smile




 

Автор: archimed7592 4.5.2007, 14:20
Anikmar, ни один менеджер памяти не рассчитан на такое... специально для этих целей придуманы boost::pool, boost::object_pool, Loki::SmallObj, etc.

Vyacheslav, волшебные контейнеры и смартпоинтеры как правило имеют automatic storage duration и потому автоматически освобождают память как только объект больше не нужен...

Добавлено через 8 минут и 51 секунду
Цитата(Vyacheslav @  3.5.2007,  12:09 Найти цитируемый пост)
  auto_aptr<char > pchar(new  MyClass[1000]);
Цитата(Vyacheslav @  3.5.2007,  12:09 Найти цитируемый пост)
Уверяю Вас, что и в первом и во втором случае  код "абсолютно" надежен, несмотря на то что в том и другом случае  используются new[] и delete[]
не откомпилится, а если сделать явное приведение указателей, чтобы компилилось, то надёжным уж явно не будет...

Цитата(Vyacheslav @  3.5.2007,  12:09 Найти цитируемый пост)
если  я этого опять же из-за кривизны рук не сделаю, то получу на этот раз абсолютный  (см АБСОЛЮТНЫЙ прил. 2. Достигший высшего предела; полный, совершенный. ) неработающий код smile
не знаю как обстоят дела QT, но в boost для это предусмотрены shared_ptr и shared_array...

Добавлено через 11 минут и 55 секунд
Цитата(Vyacheslav @  3.5.2007,  12:09 Найти цитируемый пост)
char *pchar = new [1000];
проглядел... нужно new char [1000]

Автор: Anikmar 4.5.2007, 14:38
Цитата(archimed7592 @  4.5.2007,  14:20 Найти цитируемый пост)
Anikmar, ни один менеджер памяти не рассчитан на такое... специально для этих целей придуманы boost::pool, boost::object_pool, Loki::SmallObj, etc.

Это понятно, если конечно приведенные контейнеры реализовывают это не через те же new и delete.
Просто vector, например, тут не поможет точно - я смотрел, там все через new и delete сделано, поэтому проблему фрагментации он не снимет.

По поводу приведенных классов - ничего не могу сказать, не знаю.

Там проблему пришлось решать на сколько я помню именно через свой менеджер памяти.

Автор: Vyacheslav 4.5.2007, 15:03
Цитата(archimed7592 @  4.5.2007,  14:20 Найти цитируемый пост)
не откомпилится, а если сделать явное приведение указателей, чтобы компилилось, то надёжным уж явно не будет...

Естественно smile  Опечатался 
Имелось в виду
Код

auto_aptr<char > pchar(new char[1000]);

по аналогии с первым вариантом



Автор: archimed7592 4.5.2007, 15:12
Цитата(Anikmar @  4.5.2007,  14:38 Найти цитируемый пост)
Это понятно, если конечно приведенные контейнеры реализовывают это не через те же new и delete.
Просто vector, например, тут не поможет точно - я смотрел, там все через new и delete сделано, поэтому проблему фрагментации он не снимет.
неа... vector как раз рассчитан на это... есть такая штука, называется allocator... передаётся вторым или третьим шаблонным параметром вектору... по умолчанию - это std::allocator, который использует new/delete... есть два выхода из этой ситуации: перегрузить операторы new/delete (как это сделано в Loki::SmallObj - просто наследуешь от него и будет использоваться специализированный менеджер памяти), но это не всегда удобно - для int их не перегрузишь...
второй вариант - подменить аллокатор... например на boost::pool_aloc

Добавлено через 2 минуты и 17 секунд
Vyacheslav, и тем не менее... кривость рук в boost исправляется наличием разных смартпоинтеров для разных случаев (new vs new[]) ;)

Автор: Gelos 5.5.2007, 22:47
Мда, дискуссия разрослась. Вобщем, немного поясню суть. Как, где-то было выше сказанно, выделялось постоянно в цикле большое колличество блоков памяти. Очень большое. То есть,  в файле было около 38000 записей, все они дробились на куски, и под куски выделялась память. И этот файл обрабатывался в цикле тоже около 39 тысяч раз.  Проверка пошаговая дебагером показала, что не смотря на то, что ко всем опереторам new вызываются delete к концу обработки вложенного цикла выедается на 6кб. и так дальше. 

Причем, конструкция (схематично) была такая 
while(1)
{
ukaz = new char[MAX];

----------
код 

---------
 delete [] ukaz;

}
После изменения констукции на

Bool flag = TRUE;
while(1)
{
if(!flag)
{
delete [] ukaz;
}
ukaz = new char[MAX];
flag = FALSE;
----------
код 

---------
 
}

утечка памяти исчезла.

Автор: Vyacheslav 6.5.2007, 20:34
Цитата(archimed7592 @  4.5.2007,  15:12 Найти цитируемый пост)
Vyacheslav, и тем не менее... кривость рук в boost 

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

Добавлено через 6 минут и 37 секунд
Цитата(Gelos @  5.5.2007,  22:47 Найти цитируемый пост)
утечка памяти исчезла.

Вообще в своей схеме Вы не указали самый главный момент: где производился выход из бесконечного цикла?.  Судя по
Код

while(1)
{
ukaz = new char[MAX];

// выход где-то здесь 
//

 delete [] ukaz;
}

явно до вызов последнего вызова delete. Отсюда и утечка 

Автор: archimed7592 6.5.2007, 20:48
Vyacheslav, зачем делать класс, который можно будет использовать неправильно, когда можно сделать два класса, которые как не крути - неправильно не поюзаешь... тем более, что в с++ это решается оч простым путём стратегий и два класса делать и не придётся:
Код
template < ... > class shared_ptr_base;
typedef shared_ptr_base < ..., usual_allocation, ... > shared_ptr;
typedef shared_ptr_base < ..., array_allocation, ... > shared_array;
не знаю, как это сделано в boost, но не думаю, что идея сильно отличается...

что же касается 
Цитата(Vyacheslav @  6.5.2007,  20:34 Найти цитируемый пост)
А мне это делать не привык,потому, как за меня это буст делает" вряд ли будут приняты в качестве аргумента в серьезной компании 
никто не спорит - во-первых, делать нужно уметь делать и так и эдак, во-вторых, привычка пользоваться мощными библиотеками не освобождает от надобности знания как эти библиотеки реализованны... а вот знание внутреннего устройства библиотеки - аргумент уже намного более серьёзный

Автор: Earnest 7.5.2007, 11:10
Цитата(Vyacheslav @  3.5.2007,  13:09 Найти цитируемый пост)
Здесь уже вопрос затронут не о применении STL, а о том что  уважаемый Mayk пытается неумение написать код списать на якобы имеющиеся недостатки языковых конструкций. 

Я бы не сказала, что Mayk именно такую позицию защищает. Скорее, это твоя интерпретация его постов. А его проффесионализм и знание C++ у меня сомнений не вызывает.
Все, что я видела - это ратование за использование конструкций, облегчающих жизнь программистаи делающих C++ более безопасным. Лично я это тоже приветствую, и сама по другому давно не пишу (т.е. не помню, когда последний раз явно писала delete). 
Но у меня тоже есть ощущение, что такого рода конструкции скорее помешают начинающему программисту разобраться в языке, чем помогут. В том смысле, что у него просто не будет суровой необходимости, а любопытства может не хватить.
Это немного оффтоп, но по моему опыту, программисты, пришедшие из C, лучше въезжают и больше понимают, чем программисты, пришедшие из более высокоуровневых языков типа Жавы.
С другой стороны, если в проекте принято использование умных примочек (а я по опыту знаю, насколько это сокращает число потенциальных ошибок), то разрешать кому-то в целях обучения использовать голые new-delete
я не собираюсь...


Автор: Gelos 7.5.2007, 16:16
Vyacheslav

Хм, а знаете, может быть и так, хотя,  по идее, выход у меня тогда, когда файл закончится. Может там и реально в самом конце выход случается раньше чем остатки выделенной памяти будут скормлены Ктулху...

Автор: Vyacheslav 9.5.2007, 22:19
Цитата(archimed7592 @  6.5.2007,  20:48 Найти цитируемый пост)
Vyacheslav, зачем делать класс, который 

Извините,  но не стоит придираться к классу, который был написан на коленке для исключительно  конкретной демонстрации, причем посредством отнаследования от auto_ptr smile и который нигде кроме как в этой дискуссии не применялся smile

Цитата(Earnest @  7.5.2007,  11:10 Найти цитируемый пост)
Я бы не сказала, что Mayk именно такую позицию защищает. Скорее, это твоя интерпретация его постов. А его проффесионализм и знание C++ у меня сомнений не вызывает.

Мда ? Ну в таком случве исходя из этого совета
Цитата(Mayk @  1.5.2007,  22:52 Найти цитируемый пост)
а обязательно использовать эти ненадёжные new[]/delete[]'ы? 
контейнеры и smart pointerы не пойдут?

Вы не просветите меня , в чем заключается ненадежность new[]/delete[]. А то  я их использую, а вдруг мой код из-за них ненадежен? Нехорошо как то получается smile  А так же за одно просветите о ненадежности векторов. 
 Вообще в таком случае  "ненадежность"  new[]/delete[] я предлагаю решить кардинальным способом: мигрировать на С# ну или в крайнем случае на managed C++. Как Вам такой совет smile ? На мой вгляд он ни чем ни хуже перехода на  smart pointerы. Кстати  и изучение  еще одного языка также весьма может поднять профессионализм: если раньше человек не знал одного языка, то после с таким подходом будет не знать целых два. Прогресс налицо smile


Цитата(Earnest @  7.5.2007,  11:10 Найти цитируемый пост)
С другой стороны, если в проекте принято использование умных примочек (а я по опыту знаю, насколько это сокращает число потенциальных ошибок), то разрешать кому-то в целях обучения использовать голые new-delete
я не собираюсь...

А я  не мешаю использовать их другим во вполне серьезных проектах. И не  уверен, что отсутствие прямого вызова new-delete - признак ученического проекта. Не барское дело ломать стиль программирования своего подчиненного  через коленку, даже если и имеешь на это право. Со временем тот и сам поймет, как улучшить свой код.   Если программист что-то применяет, то он должен применять это осознанно и по делу, а  не потому что без этого он не способен написать грамотный код или потому что начальник сказал, что вот это круто. 
И кстати , мой Вам респект. Как Вам удается найти на работу столь профессиональный контингент, которые совершенно не требуют обучения? Меня на фирме привлекают очень часто в качестве технического интервьюера при приеме кандидатов и искренне рад, когда из 5 человек  хотя бы 1 показывает приемлимый уровень знаний  С++ в объеме, ограниченным книгой Страутрупа первого издания.  

Цитата(Earnest @  7.5.2007,  11:10 Найти цитируемый пост)
Лично я это тоже приветствую, и сама по другому давно не пишу (т.е. не помню, когда последний раз явно писала delete). 

smile  То есть если есть необходимость например использовать контейнеры с указателями, Вы заменяете их на контейнеры со smart  pointrerами? А я все по старинке :( : шаблонный функтор со стратегией для удаления и for_each.

 Ну и возвращаясь к нашей ситуации, когда человек спрашивает, почему у него происходят утечки и получает в ответ
Цитата(Mayk @  1.5.2007,  22:52 Найти цитируемый пост)
а обязательно использовать эти ненадёжные new[]/delete[]'ы? 
контейнеры и smart pointerы не пойдут?

выглядит весьма странно. Я не собираюсь оценивать профессионализм отвечающего, но  от совета попахивает немного ламерством
Вообще то,  задав  вопрос
Цитата(Vyacheslav @  2.5.2007,  13:37 Найти цитируемый пост)
А где в стандарте описывается вероятность, с которой должен "ненадёжный" delete[] должен освобожать память выделенную "ненадёжным" new[]   

я ожидал, что Mayk уточнит понятие "ненадежности" примерно так, как это сделал Anikmar
Цитата(Anikmar @  2.5.2007,  14:18 Найти цитируемый пост)
Пожалуй слово "ненадежный" надо употреблять к программеру.

и статус-кво злосчастных new[]/delete[]  и языка С++ в целом было бы восстановлено. Но уважаемый  Mayk упорно стал  защищать свою первоначальную формулировку,  присовокупив  к компании ненадежных еще  vector smile  только на том основании, что он не вызывает delete, если его элеметами являются указатели на объекты.

PS Gelos , прошу ни в коем случае не принимать данную дискуссию на свой счет. Это чисто теоретический спор.

 

Автор: skyboy 9.5.2007, 23:01
выдержка из книги "Наука отладки": http://217.16.26.161/programming/digest/scofdebug/index.shtml
Это к тому, что не стоит настоятельную рекомендацию "более безопасных" конструкций воспринимать как потакание ламеризму и личную обиду smile просто порою важнее получить корректно работающий продукт с дополнительными "гарантиями" корректности, нежели потешить самолюбие разработчика("80 000 строк кода без единой отладки - и все правильно работает") ;) прошу не воспринимать последнее сказанное, Vyacheslav, на свой счет - обидеть не хотел.  Просто мне показалось, что причина "вспышки ярости" несравнима с поводом, а "повод" интерпретирован немного неверно smile

Автор: v_nikolaev 10.5.2007, 12:21
Цитата(Earnest @ 2.5.2007,  19:11)
Кроме утечек есть ведь такая вещь, как фрагментация памяти...

тут, наверное, следует говорить о фрагментации вместе с утечками
утечки памяти могут её усугублять:
пусть у нас хороший менеджкр хипа, который возвразщает операционке свободный непрерывный участок в конце своей памяти, но всё равно неподчищенная память мешает этому - на следующей фазе программа будет выделять опять то, что было невычищено - это усилит фрагментацию определённым образом. потом мы захотим выделять большие массивы, получится, что у нас нет возможности переиспользовать то, что зарезервировано.
я имел дело с прогой в VC, в которой сложилась такая ситуация - из-за невычищенного мегабайта выделялись новые 400M. это также сопровождалось интересным эффектом - d-версия работала быстрее release smile

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


Автор: Earnest 10.5.2007, 13:15
Цитата(Vyacheslav @  9.5.2007,  23:19 Найти цитируемый пост)
И кстати , мой Вам респект. Как Вам удается найти на работу столь профессиональный контингент, которые совершенно не требуют обучения?

А мне и не удается... Я их учу... А потом они сбегают в Москву, на ЗП, которую наша контора платить не может... :(

Цитата(Vyacheslav @  9.5.2007,  23:19 Найти цитируемый пост)
 То есть если есть необходимость например использовать контейнеры с указателями, Вы заменяете их на контейнеры со smart  pointrerами? А я все по старинке :( : шаблонный функтор со стратегией для удаления и for_each.

Да, всегда, если именно контейнер владеет объектами, а не просто хранит ссылки на объекты, живущие в другом месте.
Основание: да хотя бы применение удаляющих алгоритмов, типа remove_if. Где потом концы искать, если это динамические указатели?
Ваш функтор решит проблему только удаления на деструкторе (и в тех местах, где его можно явно включить - и еще не забыть надо это сделать).
Даже если я заранее не уверена, что буду применять алгоритмы, которые могут дублировать или удалять объекты, я все равно предпочитаю безопасные контейнеры, т.к. это проще, чем ломать себе голову, думая о последствиях. Более того, если объекты не полиморфные, я предпочитаю хранить в контейнере именно объекты, а не указатели, и черт с ним, с лишним копированием.
Все это позволяет сосредоточиться на алгоритме и логике. Раньше я напрягалась по поводу "лишних" действий, которые придется выполнять бедной железке. И сейчас иногда жаба душит. Но практика показывает, что реальные тормоза возникают вовсе не из-за такой фигни, а скорее из-за плохого, слишком тупого, алгоритма. А чем сложнее алгоритм, тем "самостоятельнее" должны быть данные.

Цитата(Vyacheslav @  9.5.2007,  23:19 Найти цитируемый пост)
Не барское дело ломать стиль программирования своего подчиненного  через коленку, даже если и имеешь на это право. Со временем тот и сам поймет, как улучшить свой код

Пока он будет сам это понимать, пользователи будут присылать сообщения об ошибке: "ваша программа попросила послать разработчику..."
Если трепетно относиться к такому стилю, так что же тогда сказать о манере называть переменные и расставлять скобки? 
Свов код пусть пишет как хочет (у себя дома), а код в проекте не его, а общий. И, скорее всего, поддерживаться будет совсем другими людьми.

А насчет Mayk'а, давай не будем его обсуждать: сам он к спору интерес явно потерял.

Автор: Vyacheslav 10.5.2007, 13:44
Цитата(Earnest @  10.5.2007,  13:15 Найти цитируемый пост)
Пока он будет сам это понимать, пользователи будут присылать сообщения об ошибке: "ваша программа попросила послать разработчику..."
Если трепетно относиться к такому стилю, так что же тогда сказать о манере называть переменные и расставлять скобки? 

Это как раз определяется внутрикорпоративным стандартом кодирования smile .  Только стандарт кодирования не затрагивает обязательность применения тех или иных приемов smile, как Вы понимаете.  Более того, в слкчае офшорного программрования приходится использовать стандарт кодирования конкретного заказчика, но и там я ни разу не встречал рекомендации обязателной замен пары new/delete smart pointer'ами.

Цитата(Earnest @  10.5.2007,  13:15 Найти цитируемый пост)
Пока он будет сам это понимать, пользователи будут присылать сообщения об ошибке: "ваша программа попросила послать разработчику..."

Это как  раз из другой оперы.  И  какже быть с циклом разработки. в который неотъемлимой частью входит тестирование? 

Цитата(Earnest @  10.5.2007,  13:15 Найти цитируемый пост)
Ваш функтор решит проблему только удаления на деструкторе (и в тех местах, где его можно явно включить - и еще не забыть надо это сделать).

Забыть можно все что угодно, например тот же кошелек  дома. Но это еще не повод, чтобы  с вечера писать у себя на лбу : "Не забыть взять кошелек "  smile .  И если факт такой забывчивости вещь для конкретного программиста случайная, то ничего страшного. Свою ошибку он найдет сам при проведении unit тестирования, или его коллеги тестировщики.  И при нахождении ошибки код программы будет таким эе надежным, как и  с примененим строгих и умных указателей. А вот если для него такая забывчивость в порядке вещей, то тут надо не   переходить на более прогрессивные приемы программирования, а менять профиль работы. 

Автор: Earnest 11.5.2007, 17:27
Цитата(Vyacheslav @  10.5.2007,  14:44 Найти цитируемый пост)
Забыть можно все что угодно

Забыть - это только одна сторона проблемы. Согласна, не забывай и все будет хорошо. Но ты ловко обошел более важную часть: что делать там, куда ручки програмиста просто не дотянутся, например, повторю, при применении алгоритмов типа revove_if. Там хорошая память не спасет.
Кроме того, если мы беремся корректировать чужой код, про это надо еще и узнать как-то.

Цитата(Vyacheslav @  10.5.2007,  14:44 Найти цитируемый пост)
И  какже быть с циклом разработки. в который неотъемлимой частью входит тестирование? 

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

Но самое главное тем не менее не это. Все эти умные примочки эфективно сокращают количество написанного руками кода. Это я не к тому, что стучать по клавишам лень. А к тому, что лишние технические детали загромождают алгоритм и скрывают его логику. Конечно, структурировать код можно хорошо и без умных примочек, только усилий нужно приложить больше. А, спрашивается, ради чего? Если есть известные способы избежать лишних траблов?

Цитата(Vyacheslav @  10.5.2007,  14:44 Найти цитируемый пост)
но и там я ни разу не встречал рекомендации обязателной замен пары new/delete smart pointer'ами.

Этот аргумент не кажется мне, мягко говоря, убедительным. Разве это означает, что эта практика плоха? 

И в чем, в конце концов, проблема? Ну пусть конкретный программист пока не знает о RAII. Ну так я расскажу и покажу. А потом попрошу переписать его код. Если код изначально хорош, это совсем нетрудно. 

Автор: Vyacheslav 12.5.2007, 19:10
Цитата(Earnest @  11.5.2007,  17:27 Найти цитируемый пост)
Но ты ловко обошел более важную часть: что делать там, куда ручки програмиста просто не дотянутся, например, повторю, при применении алгоритмов типа revove_if. Там хорошая память не спасет.


Но Вы тоже ловко обошли  вопрос: ведь не всегда легко можно заменить указатели на сами объекты smile. Вы думаете , я  испытаю настолько большие затруднения remove_if? А мне кажется, что выкручусь весьма элементарно smile   Предикат мне все равно нужно будет написать, так что я просто добавлю в предикат инструкцию delete и воспользуюсь вместо связки erase/remove_if связкой  erase/copy_remove_if
примерно так
Код

#include <vector>
#include <algorithm>
#include <iostream>
struct Class {
    Class(){
                    static int count = 0;
        number_ = ++count;
        buffer_ = new char[count];
    }
    ~Class(){ 
        delete[] buffer_;
        std::cout << "delete Class with number:" << number_ << std::endl;
    }
    int number_;
    char* buffer_;
};

bool removeOdd (Class* pClass ){
    return pClass->number_ % 2 ? delete pClass, true: false;
};

std::ostream& operator<<( std::ostream& out, Class* pClass ) {
    return out << pClass->number_;
}

void destroyClass(Class *pClass) {
    delete pClass;
}

using namespace std;
int main(int argc, char* argv[])
{
    vector<Class*> v;
    for( int i = 10; i--;) {
        v.push_back(new Class);
    }
    cout << "Vector size:" << v.size() << endl; 
    copy(v.begin(),v.end(), ostream_iterator<Class*, char>(cout," "));
    cout << endl << "remove and destroy all Class instance with odd number" << endl;
    //---------------------------------------------------------------------------------------------
    v.erase(remove_copy_if(v.begin(), v.end(), v.begin(), removeOdd ), v.end());
    //----------------------------------------------------------------------------------------------
    cout << "Vector size:" << v.size() << endl; 
    cout << "remained Class instances with number:"; 
    copy(v.begin(),v.end(), ostream_iterator<Class*, char>(cout," "));
    cout << endl << "destroy remained Class instances" << endl;
    for_each(v.begin(), v.end(), destroyClass );
    return 0;
}

Надеюсь данный пример достаточно презентативен  smile

Цитата(Earnest @  11.5.2007,  17:27 Найти цитируемый пост)
Конечно, структурировать код можно хорошо и без умных примочек, только усилий нужно приложить больше. А, спрашивается, ради чего? Если есть известные способы избежать лишних траблов?

Вы все меня стараетесь убедить, что эти "умные примочки" благо? Это напрасный труд smile Я не ретроград, и сам с Вами полностью согласен. Очень полезны и если я не скажу, что часто использую "умные указатели" , то уж "строгие указатели"  и гарды ( guard ) использую постоянно, но  только вопрос этого диспута не в том, хорошо это или плохо . О том , в чем я не соглашусь, читайте ниже
Цитата(Earnest @  11.5.2007,  17:27 Найти цитируемый пост)
Этот аргумент не кажется мне, мягко говоря, убедительным. Разве это означает, что эта практика плоха? 

Мне кажется я уперся в стену  smile .  Я говорил, что это практика плоха? Я  говорил, что это не панацея от кривых рук и совет использовать smart pointer'ы , вместо того, чтобы помочь разобраться, где ошибка - порочен. Не говоря уже об объявлении "ненадежными" new, delete, vector на том основании. что они в состоянии делать то, для чего не предназначены: а именно, думать за программиста. 

Цитата(Earnest @  11.5.2007,  17:27 Найти цитируемый пост)
И в чем, в конце концов, проблема? Ну пусть конкретный программист пока не знает о RAII. Ну так я расскажу и покажу. А потом попрошу переписать его код. Если код изначально хорош, это совсем нетрудно.  

Да проблемы нет. Но если код "изначально хорош", чего его трогать. А рассказать и показать можно. А насчет переписать, не знаю. Если код удовлетворяет требуемому уровню безопасности и не имеет дефектов, то смысл его переписывать? Не лучше ли дать следущее задание, в котором  он сможет применить полученные знания. 


Автор: Earnest 13.5.2007, 08:09
Цитата(Vyacheslav @  12.5.2007,  20:10 Найти цитируемый пост)
Я говорил, что это практика плоха? Я  говорил, что это не панацея от кривых рук и совет использовать smart pointer'ы , вместо того, чтобы помочь разобраться, где ошибка - порочен.

ОК, значит мы во всем согласны. От кривых рук или ошибки в ДНК это действительно не спасает.
Насчет помочь разобраться... В принципе, конечно, правильно ты говоришь, но только теоретически. Некоторый начальный уровень должен быть достигнут самостоятельно, иначе на что это программист годится? А многие  начинающие программисты нынче так обленились со всеми этими супер-средами, где все "само" делается, что все им в рот положи, да и  разжуй еще, а они только стонут - ох, как неудобно и непонятно.... Поэтому и возникают отписки типа - используй STL и т.д.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)