LINUX.ORG.RU

Я познаю C++. А что, оператор delete не обнуляет указатель?

 , ,


0

4

Всегда думал, что конструкция:

delete obj;

высвободит память и обнулит указатель obj, чтобы его далее можно было проверять на NULL и вообще.

А оно походу совсем не таг.

★★★★★

Ответ на: комментарий от Xintrea

Вот сейчас думаю, как выкрутится из этой ситуации.

Как тебе уже неоднократно написали, нужно использовать unique_ptr/shared_ptr. Про RAII, ты, похоже, и так знаешь. Если тебя интересуют вопросы производительности, то unique_ptr практически то же самое, что и голый указатель (но реализует семантику владения, т.е. ненулевой указатель будет только у одного владельца, разделять владение не получится), shared_ptr заметно тяжелее, но он реализует семантику разделения владения, причем безопасную при конкурентном использовании, если такое реализовывать на указателях, не факт, что осилишь с меньшими накладными расходами.

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

Объект должен удаляться в деструкторе коллекции объектов.

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

prefetch
()
Ответ на: комментарий от i-rinat

Один из полезных комментариев в треде)))

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

И да, в таких случаях незазорно руками обнулить указатель.

быдлокодер детектед

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

Вот сейчас думаю, как выкрутится из этой ситуации.

для тебя — shared_ptr

/thread

anonymous
()

2015
NULL, когда есть nullptr; да и вообще new/delete, когда есть умные указатели

ktan ★★★
()
Последнее исправление: ktan (всего исправлений: 1)
Ответ на: комментарий от unt1tled

Даже в 2015 есть микроконтроллеры, чем и является ниша сишки.

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

hateyoufeel ★★★★★
()

высвободит память и обнулит указатель obj, чтобы его далее можно было проверять на NULL и вообще.

#define CC_SAFE_DELETE(p)    do { delete (p); (p) = nullptr; } while(0) 
Solace ★★
()
Ответ на: комментарий от crutch_master

Наркоман штоле сука?

в С++ ничего само не происходит

разве что неявное приведение типов

какие проблемы, use workaround, Luke

ok!

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

Разве что объекты преобразуются из типа в тип и обратно, но это такой пустяк!

Они преобразовываются там, где дали такую возможность, и где этой возможностью явно воспользовались. Если ты передаешь char* в функцию, которая принимает std::string, и считаешь, что так делать ненужно, - просто не делай так.

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

workaround

Сам ты нарк, если у тебя 2 флага - workaround. Неявные преобразования это не фича языка, а лишь дефолтная опция компилятора, потому что всем так удобно. Не удобно, возьми, да выключи, а то орешь, как будто тебя заставили скрипт на пёрле писать.

crutch_master ★★★★★
()
Последнее исправление: crutch_master (всего исправлений: 2)

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

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

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

Вызов виртуального метода стоит аж в полтора раза дороже обычного. Не считая того, что актуальные версии gcc/clang с LTO во многих случаях могут их оптимизировать в обычные. Таблицы вообще лежат отдельно в единственном экземпляре для класса, а не каждого объекта. Так что не надо тут газифицировать лужи.

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

Оптимизироваться будут вызовы в том случае если вызывается виртуальный метод, от имени объекта для типа которого он был определен. Если вызывать через базовый ,от накладные расходы весьма существенные. Если бы это было не так, не надо было бы всю STL делать на шаблонах.

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

Оптимизироваться будут вызовы в том случае если вызывается виртуальный метод, от имени объекта для типа которого он был определен. Если вызывать через базовый ,от накладные расходы весьма существенные.

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

Если бы это было не так, не надо было бы всю STL делать на шаблонах.

А что в STL можно было бы сделать не на шаблонах? (упустим то, что само название говорит о шаблонах)

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

в веркере! ты что, читать разучился?

anonymous
()

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

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

С учетом того, что шаблоны по факту реализуют статический полиморфизм (не беря в расчет метапрограммирование с boost'ом), то все можно сделать и на виртуальных функциях, но более тормозно и коряво (что ИМХО т.к. не видел в живую примеров). Как-то делал тесты (очень давно) и разрыв был вроде бы примерно на пол порядка, но за данные как и за их актуальность не ручаюсь. Для себя я тогда решил, что для языка, в котором за скорость платишь всем остальным, применение виртуальных функций - крайняя мера. К вопросу что можно сделать без шаблонов. Думаю что все тоже самое, но с применением еще большего множества абстракций и повальным применением виртуальных функций - динамического полиморфизма, что угробило бы все преимущества.

ЗЫ Что за оптимизация? Как оптимизируются вызовы реализованные в своем большенстве через дополнительные указатели (vtable) на методы «неизвестсных» на момент вплоть до самого вызова объектов?

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

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

Вот именно что коряво. STL вообще старше С++ и был придуман именно под обобщенное программирование, а не скорость.

ЗЫ Что за оптимизация? Как оптимизируются вызовы реализованные в своем большенстве через дополнительные указатели (vtable) на методы «неизвестсных» на момент вплоть до самого вызова объектов?

Благодаря LTO у нас есть все возможные варианты. В том числе варианты вызовов и можно определить - что из «неизвестных» может попасть в место вызова. Если для полученного подмножества вызывается один и тот же метод - PROFIT. А если пользоваться final, то в простых случаях и LTO не нужен.

anonymous
()

и обнулит указатель obj

Бесполезная операция

чтобы его далее можно было проверять на NULL

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

и вообще

Операция delete уничтожает объект(или группу объектов в случае delete[]) и освобождает память(хотя теоретически, в случае опционального сборщика, это может быть не так). Указатель, по которому мы вызываем delete, не представляет сам по себе никакой ценности, таких указателей может быть много, обнулять его совершенно бессмысленно. Если тебе очень нужно, обнули сам, но скорее всего, ты ошибся при проектировании.

Ну и да, если это пользовательский код, то используй умные указатели.

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

Чтобы при повторном delete небыло сегфолта, например.

Еще одно открытие для тебя:

/* никогда не даст сегфолта */
int main() { delete (void *) NULL; }

Домашнее задание: найти кусок стандарта, описывающий почему.

KennyMinigun ★★★★★
()
Последнее исправление: KennyMinigun (всего исправлений: 1)
Ответ на: комментарий от Solace
#define CC_SAFE_DELETE(p)    do { delete (p); (p) = nullptr; } while(0)

Ну йопта! C++ (14) на дворе, а ты забагованные макросы предлагаешь.

template <typename T>
inline void cc_safe_delete(T *ptr) {
    delete ptr;
    ptr = nullptr;
}

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

ptr = nullptr;

Fail. Ну и если С++ (14), то можно было бы и отдельно массивы обрабатывать.

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

Ну так он о том и пишет. Что если будет зануление, то сегфолта не будет.

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

Настоящие пацаны используют только new.

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

Мда... Надеюсь, что ты просто опечатался.

Ах, да, тупая опечатка амперсандом:

template <typename T>
inline void cc_safe_delete(T *&ptr) {
    delete ptr;
    ptr = nullptr;
}

KennyMinigun ★★★★★
()
Последнее исправление: KennyMinigun (всего исправлений: 2)
Ответ на: комментарий от anonymous

Если ты передаешь char* в функцию, которая принимает std::string, и считаешь, что так делать ненужно, - просто не делай так.

Так можно до JS докатиться и призвать анонiмуса.

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

Ну либо это должна быть не free(void *ptr), а free(void **ptr) и вызывать её придётся иначе.

void zfree(void **obj);
#define free(obj) zfree((void **)&obj)

Хотя это уже изврат.

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

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

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

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

Избранные теги: быдлокод, говнокод, костылестроение, костыли, костыль, костыль на костыле, криворукость, кривые руки

staseg ★★★★★
()
Последнее исправление: staseg (всего исправлений: 1)
Ответ на: комментарий от staseg

В любом случае называть возможность (причём, в большинстве случаев отключаемую) писать вместо

char c = 5;
int i = (int)c;
,
char c = 5;
int i = c;
фичей, это слишком громкое заявление. Перегрузка операторов, функций/методов, ООП - это да, фичи.

crutch_master ★★★★★
()
Последнее исправление: crutch_master (всего исправлений: 1)
Ответ на: комментарий от staseg

Неявные преобразования типов

Да и в каком месте они неявные, если кодер сам присваивает к int char или вызывает функцию/метод с левыми аргументами?

crutch_master ★★★★★
()
Последнее исправление: crutch_master (всего исправлений: 1)
Ответ на: комментарий от crutch_master

Перегрузка операторов, функций/методов, ООП - это да, фичи.

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

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

Макрос короче :)

А еще с макросами веселее, ведь они умеют размножать вычисления:

CC_SAFE_DELETE( *p++ );
CC_SAFE_DELETE( *foo() );

Ведь так весело сначала удалить одно, а потом занулить второе. Или просто что-нибудь повторить.

anonymous
()

сколько лет прошло все о том же))

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

построен поверх перегрузок операторов

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

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

delete obj;

delete можно удалить нафиг, если памяти много )

Можно не освобождать память, но деструктор нужно вызывать обязательно :)

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

void func(char* _c)
{
delete[] _c;
_c = nullptr;
}

...

char* c = new char[100];
func(c);
if(c) std::cout<<"Y";
else std::cout<<"N";

как вы думаете, каков будет результат?

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

как вы думаете, каков будет результат?

+ один к тем, кто не умеет в ссылки?

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