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++ деструкторы?

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

Оберну в try-catch(...).

И..? Дальше-то что? Обрабатывать эту ошибку в деструкторе? Sql connection выкинул Could not close, в деструкторе что будете делать?

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

неужели так сложно понять, что файл - это ненадежное хранилище? как и udp и даже tcp сокет - ненадежны. Надо убедиться что все данные приняты/сохранены прежде чем их закрывать.

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

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

И для файлов подходит, если не нужна «особая» обработка. Мне в 99% случаев не нужна. Поэтому полагаться на деструктор файлового класса удобно «и 95% безопасно»

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

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

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

Так что подумайте, ответьте на поставленные вам вопросы и сами поймете, что к чему.

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

деструкторы нельзя считать универсальным надёжным способом возврата ресурсов

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

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

По поводу остального, как и следовало ожидать, возражений нет.

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

Поэтому полагаться на деструктор файлового класса удобно «и 95% безопасно»

Не приемлемый компромисс :-) Но так делают, да :-)

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

если не нужна «особая» обработка

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

Для того, чтоб очистить память — ок, но не для подобных вещей.

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

А что конкретно ты будешь делать вне конструктора? Какие действия? Нет, даже не так, на сколько действия различны в разных контекстах?

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

Ну читай если тебе насрать, запишется оно или нет.

Так что делать если не запишется? Еще раз попробовать, так ты и в деструкторе можешь еще раз попробовать. В чем проблема? Проинформировать пользователя? Можешь в деструкторе. Логи, можешь. Да хоть хитрые обработчики вызывай из деструктора, хоть в другом потоке. Да что угодно делай.

Вопрос в другом, на сколько контекст может быть разным, что бы каждый раз реакция на неудачное закрытие была разной. Я думал это типовая ситуация и в деструкторе ее можно стандаратно обработать. При чем тут контекст?

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

про точно такие же ситуации, но в деструкторах.

То есть в деструкторе ви сможете записать данные еще раз или проверить и повторить транзакцию?

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

Повторю, специально для вас:

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

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

То есть в деструкторе ви сможете записать данные еще раз или проверить и повторить транзакцию?

А почему нет?

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

а не в деструкторе, где ни открыть заново и попробовать записать еще раз, ни повторить транзакцию.

Почему еще раз то не записать? И сколько попыток еще раз сохранить ты будешь делать в контексте? Две для обычных данных, три для поважнее, четыре для совсем важных?

И еще раз, кто мешает сделать еще одно попытку в деструкторе?

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

А почему нет?

Откуда в деструкторе возьмутся данные? Хранить список всех записанных строк в объекте файла?

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

" в деструкторе ... проверить и повторить транзакцию?"

говнокодеры в трэде, все в серверную.

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

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

И еще раз, кто мешает сделать еще одно попытку в деструкторе?

Попытку то сделать можно. Речь идёт о гарантиях освобождения ресурсов. Вот что делать в таком случае:

// Может кинуть исключение в случае провала.
void save_in_database(Connection server1)
{
  Transaction t(server1);
  // ...
  // t выходит из области видимости и отправляет COMMIT на server1.
  // Отправка COMMIT на сервер server1 провалилась. Исключение
  // деструктор кинуть не может. Что делать?
}
При таком дизайне класса Transaction, вызывающий код не может точно быть уверенным, что транзакция была успешной. Это вот такая демострация RAII на деструкторах :-) Решение этой проблемы не подскажешь? :-)

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

А что ты будешь делать без RAII, попробуешь еще раз? А если опять неудача? Еще раз? Сколько раз? 5?

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

Не использовать RAII там где контекст требует особой обработки.

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

А ты не подскажешь, каким боком COMMIT, в транзакции, относится к RAII?
По мне это другой уровень абстракции.

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

А что ты будешь делать без RAII

Без RAII он, наверное, пошлет исключение соответствующего типа в вверх по стэку вызывающей функцию, которая сможет сообразить, что с server1 не так и что делать. В деструкторе ты получишь тихий обосрамс и в лучшем случае логгирование.

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

А что ты будешь делать без RAII, попробуешь еще раз? А если опять неудача? Еще раз? Сколько раз? 5?

Ты не понял походу :-) Мне надо сообщить в вызывающий код, что транзакция провалилась :-) Из деструктора я это сделать не смогу :-)

Не использовать RAII там где контекст требует особой обработки.

Правильно. Об этом я и говорю :-) Дальше какого-нибудь примитивного delete impl деструкторы не очень :-)

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

При таком дизайне класса Transaction

Класс Transaction, который в деструкторе инициирует COMMIT, а не ROLLBACK?

Конечно, после такого только и остается, что свалить вину на C++.

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

А ты не подскажешь, каким боком COMMIT, в транзакции, относится к RAII?

По мне это другой уровень абстракции. Подскажу :-) Транзакция - это временный объект в СУБД, т.е. ресурс, который управляется клиентом :-) Для его закрытия нужно отправить COMMIT/ROLLBACK :-)

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

Интересно, ты всегда так шлангуешь, когда сливаешься? Или софизм — хлеб фанатика?

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

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

можно поставить флажок например. например так делает QSaveFile из Qt:

QSaveFile is an I/O device for writing text and binary files, without losing existing data if the writing operation fails.

While writing, the contents will be written to a temporary file, and if no error happened, commit() will move it to the final file. This ensures that no data at the final file is lost in case an error happens while writing, and no partially-written file is ever present at the final location.

If commit() was not called and the QSaveFile instance is destroyed, the temporary file is discarded.

фу быть таким тупым.

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

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

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

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

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

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

Класс Transaction, который в деструкторе инициирует COMMIT, а не ROLLBACK?

Что-что? :-) Ты хочешь сказать, что можно забыть написать t.commit() и будет попытка отката? :-) Лол :-) Ну если commit() вынести в метод, то ROLLBACK в деструкторе уже вообще не нужен :-) Собственно, как и сам деструктор :-)

Конечно, после такого только и остается, что свалить вину на C++.

Никто твой любимый C++ не винит :-) Не переживай :-)

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

в таких случаях:

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

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

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

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

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

можно поставить флажок например. например так делает QSaveFile из Qt:

Хахаха :-) Лол :-) А как же идиоматический способ сообщения об ошибках через исключения? :-)

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

А что делать? Из деструктора можно послать сигнал асинхроному обработчки кстати тоже.

Добро пожаловать в Node.js :-)

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

А как же идиоматический способ сообщения об ошибках через исключения? :-)

всё таки какой хороший язык С++: таких идиотов отсеивает.

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

всё таки какой хороший язык С++: таких идиотов отсеивает.

В этом плане, ему за C вообще не угнаться :-)

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

сюрприз: rollback тоже может выдать ошибку

Он думает, что ROLLBACK можно проигнорировать :-) Но он не учёл, что можно также забыть вызвать t.commit() и вызывающий код всё так же останется в неведении :-) Т.е. в данном случае, всё сводится к явным вызовам функций без участия деструкторов:-)

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

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

Так вы определитесь, что вы хотите: иметь возможность выполнить операцию в деструкторе или же иметь возможность вернуть результат операции из деструктора. Первое вы сделать можете, просто коды ошибок придется подавлять. Второе — только через костыли в виде соглашения об использовании errno или чего-то вроде этого.

Тем не менее, выполнять осмысленные действия в деструкторе можно.

иначе всё начинает работать на честном слове

Ой, вы такой умный. Может вы решите таки задачку с N файлами, при закрытии i-го из которого возникает ошибка?

А то ведь это совсем простой случай для более общей проблемы: есть N действий, которые _нужно_ выполнить. На i-ом действии возникает проблема, что делать с оставшимися (N-i) действиями?

Предложите что-нибудь вменяемое, плз.

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

сюрприз: rollback тоже может выдать ошибку

И, если эта ошибка разработчику нахрен не сдалась, то зачем о ней кого-то информировать? А если разработчику знать об ошибке нужно, то почему же он не дернул rollback вручную?

Посмотрите на код:

void doSometing(Connection & c) {
  Transaction trx(c);
  ... // Bla-bla-bla.
}
У разработчика полно возможностей сказать о том, что он хочет, в явном виде: хочет — дергает commit, хочет — дергает rollback. И получает при этом всю полноту власти и информации.

А вот если что-то пошло не так, если разработчик — это анонимный смайлик с LOR-а, и не вызвал сам ни commit, ни rollback, то деструктор может попробовать подчистить ресурсы за этим интеллектуалом. Но без каких либо гарантий.

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

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

Правильной дорогой идёшь :-)

Да я в общем-то никогда не отрицал, что у «лиспов» есть ряд преимуществ/интересных особенностей. И заинтересовался не именно сейчас и не впервые, так что не надо это во «внезапные прозрения» записывать. (:

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

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

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

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

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

А вот если что-то пошло не так, если разработчик — это анонимный смайлик с LOR-а, и не вызвал сам ни commit, ни rollback, то деструктор может попробовать подчистить ресурсы за этим интеллектуалом. Но без каких либо гарантий.

я бы в деструкторе в данном случае вызывал assert(false). чтобы «смайлик с LOR-а» не полагался на то, что за него кто-то обработает завершение транзакции.

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

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

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

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

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

Покажите код, плиз.

И, заодно, попробуйте ответить на пару простых вопросов:

1. Как вы будете отдавать список незакрытых файлов наверх?

2. Что именно кто-то наверху будет делать с этим списком?

я бы в деструкторе в данном случае вызывал assert(false).

assert(false) в release-сборках не работают. В принципе.

чтобы «смайлик с LOR-а» не полагался на то, что за него кто-то обработает завершение транзакции.

Транзакция — это такая штука, которая либо явным образом завершается успешно, либо же откатывается. Соответственно, явно следует вызывать именно commit. Во всех остальных случаях, если что-то пошло не так, следует дернуть rollback. И у вас есть выбор:

- либо вы программируете в стиле goto cleanup и натыкаетесь на грабли в ситуациях, когда забыли написать goto или не подумали про исключение, которое может вылететь где-то в коде;

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

Полагаю, вы предпочитаете действовать по первому варианту. Правильно?

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

Ещё есть std::bad_weak_ptr :-)

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

С std::async хитрее, согласен. Хотя тоже можно обойти, ну или давай более конкретные случаи рассматривать.

Про локаль - ок.

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