LINUX.ORG.RU

Деструкторы не нужны (?)

 


0

5

Тут в соседней теме один анон сказанул следующее:

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

Собственно, хотелось бы поговорить о выделенном.

Антон прикрылся ссылкой, по которой про деструкторы я так ничего и не нашёл. Более того, в твиттере Кармака всё выглядит с точностью до наоборот — https://twitter.com/id_aa_carmack/status/172340532419375104

RAII constructor / destructor pairing and templates for type safe collections are pretty high on my list of C++ benefits over C

Кто прав? Кто виноват? И нужны ли в итоге C++ деструкторы?

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

так и есть. скорость разработки на не managed языках (C, C++) меньшая точно, и проблем создаёт больше.

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

И что дальше? fclose() закрывает файл в любом случае.

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

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

В объяснениях какой-то конторы, почему она перевела свои проекты с C++ на Java.

Этому так же есть нормальное объяснение. Изначально, когда компьютеры были слабыми, особых конкурентов у C++ не было, поэтому на C++ писали чуть ли не все. Тот же SmallTalk, о котором речь шла выше, обеспечивал более высокую продуктивность программиста, но и требовал за это более мощное железо. Что во времена массового распространения 86-х и 286-х оставляло безопасные и мощные языки, вроде SmallTalk-а не у дел.

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

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

Зато сейчас C++ остался в тех нишах, где он и должен был быть. И в области разработки нагруженного и производительного софта разница в трудозатратах в 15 раз — это что-то невероятное.

eao197 👍👍
()
Ответ на: комментарий от dzidzitop

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

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

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

очень просто. делаю close() файлу. если это деструктор, то про ошибку я не узнаю - будет сохранён не весь файл. если это status code или exception из нормальной функции, то покажу пользователю диалог - «не могу сохранить файл, попробуй-ка, дружок, сохранить его в другое место. ну или просто попробуй посмотреть, может место закончилось. а тем временем твой документ повисит в памяти, только программу не закрывай.»

вопрос: где пользователь будет нецензурно ругаться - с деструктором, или с нормальным close()?

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

Откуда вообще в сценарии взялся пользователь? Без пользователя уже никуда?

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

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

Торвальдс как-то совершенно определенно объяснил почему он предпочитает именно C: глядя на код он видит как этот код будет преобразован в инструкции процессора и его от этого, скажем кратко, штырит) Конечно, C++ с его RAII, исключениями и прочими шаблонами так однозначно в машинный код не переводится и логика работы программы от этого может быть неочевидной, поэтому под критерии Торвальдса не подходит.

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

В контексте какого стандарта/системы? Что такое «закрывает»?

http://pubs.opengroup.org/onlinepubs/9699919799/functions/fclose.html

The fclose() function shall perform the equivalent of a close() on the file descriptor that is associated with the stream pointed to by stream.

http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html

If close() is interrupted by a signal that is to be caught, it shall return -1 with errno set to [EINTR] and the state of fildes is unspecified. If an I/O error occurred while reading from or writing to the file system during close(), it may return -1 with errno set to [EIO]; if this error is returned, the state of fildes is unspecified.

HP-UX, например, не закрывает fd, если close упал с EINTR:

http://docstore.mik.ua/manuals/hp-ux/en/B2355-60130/close.2.html

[EINTR]
An attempt to close a slow device or connection or file with pending aio requests was interrupted by a signal. The file descriptor still points to an open device or connection or file.

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

очень просто. делаю close() файлу. если это деструктор, то про ошибку я не узнаю - будет сохранён не весь файл.

Хорошо, ты делаешь:

file.close()

Где ты в этом коде увидел деструктор и RAII?

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

Вопрос на засыпку: в каком состоянии находится дескриптор файла после неудачного вызова close()? Каким-нибудь стандартом оговорено что этим дескриптором после такого вызова можно пользоваться, в том числе повторно вызывать close()?

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

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

Код пишется для ЭВМ. Эта ЭВМ может стоять на автоматической межпланетной станции, покинувшей пределы солнечной системы.

Если в ТЗ описан четкий алгоритм реакции на ошибки закрытия файла, то чем тебе может помешать RAII?

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

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

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

Вот поэтому и нужно считать его закрытым с момента вызова fclose().

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

& DarkEld3r. Такой подход хорош в языках, создаваемых одной компанией или человеком вместе со всех инфраструктурой, в т.ч. продиктованным форматом сериализации. Там можно запилить интерфейс Serializable или метод __repr__.

Для C++ такое не пройдёт. Тем более, предлагалось наследоваться ОТ вектора: нечто обратное наследованию от какого-нибудь ISerializable.

Так что free functions (статические методы) для сериализации is the way to go. Так же, как перегрузка операторов ввода-вывода.

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

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

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

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

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

Повторю вопрос: в чем проблема RAII?

особенно в отсутствие finally.

finally - кривой костыль в отсутствие RAII. Почему кривой? Потому что попадая в finally ты не знаешь какие именно ресурсы были выделены и их нужно освобождать, а какие - нет.

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

Вопрос на засыпку: в каком состоянии находится дескриптор файла после неудачного вызова close()?

POSIX говорит, что если close упал с EINTR или EIO, то состояние дескриптора “unspecified”, что значит, что это нужно смотреть в документации конкретной системы. Linux всегда закрывает.

Каким-нибудь стандартом оговорено что этим дескриптором после такого вызова можно пользоваться, в том числе повторно вызывать close()?

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

Такие дела.

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

close можно вызывать с чем угодно

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

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

Ты ерунду какаю-то написал. Там нет слова descriptor вообще, и не может быть, т.к. это описание стандарта крестов, который платформонезависимый. Под Windows же кресты работают? А там нет никаких дескрипторов.

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

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

чем лучше finally - можно кидать исключения, и не париться про terminate(); finally - это всегда код «на месте», с учётом контекста. с деструктором для обработки ошибок нужно париться. а без finally много гемора с вызовом «release resource» кода. юзать RAII «на месте» - более многословная конструкция с локальными классами.

для меня тут нет однозначного победителя, хотя в плюсах мне finally не хватает точно.

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

Анонимус, ты решил устроить вечер глупых вопросов?

Upon successful completion 0 is returned. Otherwise, EOF is returned and errno is set to indicate the error. In either case any further access (including another call to fclose()) to the stream results in undefined behavior.

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

try { } finally { } довольно просто использовать автоматически, одна конструкция на один ресурс. утечек не будет:

resource = aquire();
try {
} finally {
    release(resource);
}

как тут

https://github.com/dzidzitop/ant_modular/blob/master/src/java/afc/ant/modular...

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

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

try { } finally { } довольно просто использовать автоматически, одна конструкция на один ресурс. утечек не будет:

Это как? 10 вложенных конструкций на 10 ресурсов?

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

Что будет если in.close() выкинет исключение? Просто процитируй.

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

Это как? 10 вложенных конструкций на 10 ресурсов?

да. меня не напрягает. но это минус по сравнению с RAII.

Что будет если in.close() выкинет исключение? Просто процитируй.

1) программа прекратит работу.

2) пользователь увидит что-то типа:

BUILD FAILED: An I/O error is encountered while loading the manifest of the module 'foo' ('/hello/world/foo/META-INF/MANIFEST.MF')

если бы потребовалось в другом месте проглотить ошибку, то с finally проблем нет. c RAII с выбором поведения всегда есть нюансы, для каждого класса свои.

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

Если ты в finally выкинешь исключение, то оно просто заменит собой то исключение, которое, собственно, и привело к входу в finally. И если у тебя 10 таких вложенных блоков, то пользователь увидит последнее исключение, а не самое первое, с которого всё началось. Чудесно, не правда ли? И ты ещё после этого критикуешь RAII.

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

-_-

FILE — это файловый дескриптор + буфер + мютекс + что-то там ещё. fclose вызывает close над файловым дескриптором, который, как я уже писал, может и не закрыть его. Это и будет утечкой файлового дескриптора. Не?

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

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

я не критикую RAII - его использую постоянно. но у него есть ограничения и нужно с ними как-то жить. finally + RAII для меня бы заменили catch (...) и неудобства от полужёсткого требования на noexcept для деструкторов. но есть только RAII + catch (...), что не идеально, но жить можно.

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

FILE — это файловый дескриптор + буфер + мютекс + что-то там ещё. fclose вызывает close над файловым дескриптором, который, как я уже писал, может и не закрыть его. Это и будет утечкой файлового дескриптора. Не?

Анонимус, ты смысл последнего предложения по ссылке, до которого ты докопался, понимаешь? Деструкторы не нужны (?) (комментарий)

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

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

C RAII тоже есть выбор, просто код нужно писать чуть иначе. Например, с использованием future. В чем проблема?

я не критикую RAII - его использую постоянно. но у него есть ограничения и нужно с ними как-то жить. finally + RAII для меня бы заменили catch (...) и неудобства от полужёсткого требования на noexcept для деструкторов. но есть только RAII + catch (...), что не идеально, но жить можно.

Ты просто не осознаешь, что finally - это костыль языка, у которого время жизни объектов не определено. И полноценной заменой RAII finally не является. Ты не можешь наоткрывать в объекте класса на java ресурсов и привязать их ко времени жизни этого объекта, ты вынужден за всеми ресурсами следить вручную и не забывать вызывать метод, который будет их освобождать.

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

проблема в том, что когда нет finally, а нужно гарантированно вызвать file.close() и не потерять исключение, внешнее или от самого file.close(), то код становится значительно сложнее, чем acquire(); try {} finally { release(); }

а поскольку я исключения проглатываю в очень специфических случаях, то банально неудобно на плюсах писать такой код. RAII не очень подходит, а finally достаточно хорош.

finally - не замена RAII. это человеческая замена для catch (...). последний ужасно неудобен.

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

проблема в том, что когда нет finally, а нужно гарантированно вызвать file.close() и не потерять исключение, внешнее или от самого file.close(), то код становится значительно сложнее, чем acquire(); try {} finally { release(); }

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

Collector c;
try {
  SmartPtr<A> a{c, ...};
  SmartPtr<B> b{c, ...};
  ...
} catch(...) {
  c.throw_if_any();
}

C++ просто гораздо гибче и великолепно расширяется собственными средствами.

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

Безусловный COMMIT из деструктора, это, конечно, сильно!

Решение этой проблемы не подскажешь? :-)

Не допускать таких как ты к программированию.

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

Безусловный COMMIT из деструктора, это, конечно, сильно!

Даже если ты сделаешь условный COMMIT из деструктора, решение не станет от этого сильнее :-)

Не допускать таких как ты к программированию.

Это не решит проблему (которой у меня нет, т.к. мне деструкторы не нужны), и не сделает RAII в C++ на деструкторах универсальным и безопасным :-) Смирись :-)

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

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

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

finally - не замена RAII

Это настолько не замена, что в .NET-е появился IDisposable со специальной конструкцией using в C#, а в Java — try-with-resources. Очевидно потому, что finally просто великолепно справлялся с подобными задачами.

eao197 👍👍
()
Ответ на: комментарий от dzidzitop

Ну, у некого dzidzitop, вероятно, и справляется. Правда, почему-то в коде этого самого dzidzitop треш и угар дела расходятся со словами, но ведь это мелочи. Зачем свои слова делами подтверждать, не правда ли?

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

Но dzidzitop сказал в морг, значит в морг только finally, только хардкор, значит только finally.

eao197 👍👍
()
Ответ на: комментарий от dzidzitop

а про треш и угар - репортай баги.

Уже только в этой теме вам пару косяков указал. В вашем коде, который вы сочли достойным явлению миру.

а тут, повторюсь, хамить не надо.

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

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