Pull to refresh

Comments 30

UFO just landed and posted this here
А вы поклянётесь на С++ стандарте, что в коде вашей компании нигде не вызывается неправильный delete?
UFO just landed and posted this here
но интересно было узнать, почему, то есть я догадывался что так оно и есть, или где-то об этом читал, но теперь знаю точно.
а вы, если этот новичок спросит, почему, скажете: «не думай, пиши код!», или же предположите почему, как и я, так сейчас же вы сможете ответить точно, а если знали — то относитесь проще, кто-то незнал, почему:)
Пожалуйста, не забывайте, что описан только один из возможных вариантов развития событий.
Надо юзать shared_ptr, если не массив или shared_array для массивов. Тогда вообще не будет вставать вопрос о том, как и главное когда вызывать delete([]).
Прямым вызовом delete([]) надо пользоваться крайне аккуратно, т.к. с большой вероятностью проглядите какое-нибудь исключение и память утечет.
Да, действительно, владеющие указатели и RAII вообще — отличная штука. Только ими нужно пользоваться не «потому что нужно», а с пониманием, какие проблемы они решают. Пост как раз об одной из таких проблем.

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

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

Кстати, вот пример, когда владеющий указатель использован неверно и это привело к дефекту в программе

Примеров неправильного использования, да еще и в C++, можно на все написать. ;) Они решают одни сложности и вносят свои, но плюсы использования все же многократно перевешивают минусы. Банально, но многие этого не понимают и плодят в своих проектах велосипеды, надеясь на «бесконечную» память, «безошибочные» сторонние библиотеки, собственное совершенство и т.п.
Об этом и речь. Скажем, если разработчик не понимает, что делает, то возможна такая ситуация: разработчик написал неверный код (с владеющим указателем или без), статический анализатор ему на это указал, разработчик не пытается понять, в чем на самом деле проблема, и не ищет, как написать правильный код, а вместо этого пытается сделать так, чтобы анализатор заткнулся. Технические средства не отменяют необходимость думать.
Я согласен с тем, что shared_* — вещь полезная, но всё-таки её надо применять тогда, когда владение объекта не определено, и он действительно «shared». К сожалению, это не garbage collector. В новом стандарте есть unique_ptr, но он реализован на ещё одной новой «фиче» — «rvalue references». Такие можно и в контейнеры класть.
но всё-таки её надо применять тогда, когда владение объекта не определено, и он действительно «shared»

Разумеется.

Такие можно и в контейнеры класть.

shared_* тоже можно.
По этой причине нельзя использовать std::auto_ptr с массивами:

std::auto_ptr ptr(new MyClassWithDestructor[32]);
std::auto_ptr ptrChars(new char[256]);

Первый пример вряд ли разрушит объект правильно в деструкторе, а второй — скорее всего разрушит нормально для всех известных мне реализаций, но всё равно «это нехорошо». Причём, что прискорбно, часто мы имеем парадигму, где auto_ptr в нормальной ситуации «грабится» с помощью release(). В этом случае только при ошибках, ведущих к исключению мы имеем покоцанную память. В новой редакции стандарта использование auto_ptr не рекомендовано, но это — повод для отдельной статьи
Это классика. auto_ptr в деструкторе вызовет delete без скобок, это точно та же ситуация, что в двух примерах в посте. «Это нехорошо» — немного неудачная характеристика. И кстати, вы пытаетесь «объяснить», что произойдет.
У меня в примеренеправильный синтаксис — не заметил в предосмотре, что хабр выкинул угловые скобки, приняв их за таг. Вот:
    std::auto_ptr<MyClassWithDestructor> ptr(new MyClassWithDestructor[32]);
    std::auto_ptr<char> ptrChars(new char[256]);
Не тратить зря время на «правдоподобные» объяснения, советуют в конце статьи, состоящей из «правдоподобных» объяснений :)
В общем, справедливо. Но написать «произойдет ужос, ужос, ваша программа превратит компьютер в черную дыру» — тоже не вариант. Пришлось рассмотреть один пример. Там, кстати, говорится, что это одна из реализаций.
Наверное, стоит добавить, что место для количества элементов выделяется с учетом выравнивания структуры.
Т.е., типично добавляется sizeof (unsigned int) (4 байта). Но, если объект объявлен с __declspec(align(16)), или в нем есть SSE-шное поле, то дополнительно выделяется не 4, а 16 байт. При этом количество элементов помещается в начало дополнительного блока (первые четыре байта). (Это для MSVC. Как это сделано в GCC, смотреть еще не доводилось).
Давненько, когда начинал изучать С++ по русским учебникам (по сути голый С с фишками С++). Юзал и сам new[] и delete. Когда устроился на работу сразу научили и все разъяснили. В итоге теперь со своей стороны таких проблем нет, вообще использую довольно редко выделения массивов, для этих целей чаще контейнеры.

А вообще особого смысла не вижу в статье, уж простите, т.к. поведение зависит от компилятора и изучать какой-то один для того, чтобы понять механизм нет никакого смысла. А по поводу new/delete и new[]/delete[] уже очень много понаписано, лучше просто собрать список различных фейлов, по типу данного оператора, неправельности перегрузки, отсутствия конструктора копий и т.д…
Спасибо за статью! Для глубокого понимания предмета хороших антипримеров никогда не бывает мало.
Зачем вообще использовать new[] когда есть вектор?
Он и расширяется сам и управляет памятью сам и легко получить указатель на память.
Зачем использовать опасные методы (обычный указатель на ресурс) там, где есть хорошее безопасное решение?
Да, вы правы, во многих случаях нужно просто использовать vector. Один из случаев, когда vector неудобен, — выделение памяти для последующей передачи ее во владение сторонней библиотеке вроде zlib.
Вы правы в случае, если вы пишете high-level логику или нечто в этом роде.
ИМХО тем, кто пишет low-level код, использование stl-ные штук нужно просто запретить
Спасибо. Более десяти лет программирую на C++, правило помню — но что там детально происходит память не держала. Статья очень хорошо написано, так что, надеюсь, теперь буду помнить не только «как», но и «почему» :).
спасибо за статью
надо бы ее перенести в профильный Блог про С++
там на нее больше народу обратит внимание.

А вот использование new[] & delete — любимый конек ловушек на тестирвоание С++ прграммистов.
Вот на этой строке: «В соответствии со Стандартом C++, в этой ситуации поведение не определено» статью надо было заканчивать. Всё, что идёт ниже — вода.
Существенная часть статьи — подробное описание различных undefined behaviour. Зачем?

— Больной перед смертью потел?
— Потел.
— Это хорошо.
Зачем такие бессмысленные посты?
Столько букв чтобы сказать: неправильный delete = undefined behavior
Да, прекрасно. Вот определение UB (1.3.12): «поведение, которое может возникнуть при использовании ошибочного кода или ошибочных данных, на которое этот Международный Стандарт не накладывает никаких требований».

Это определение безупречно, но очень абстрактно. Как его понимать разработчику, который недавно пишет на C++? «Не делай так или тебя уволят»? Или, может быть, «если так сделать, будет плохо»?
Note: permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during
translation or program execution in a documented manner characteristic of the environment (with or without
the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a
diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed.

Чего тут не понять?
Only those users with full accounts are able to leave comments. Log in, please.