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

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

кстати, из наболевшего:

try (InputStream in = new BufferedInputStream(new FileInputStream("some_path"))) {
}

авторы таких конструкций допускают утечку ресурсов, ибо ресурсом владеет FileInputStream и он должен быть в переменной in.

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

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

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

Ну давайте посмотрим:

        try {
            final FileInputStream in = new FileInputStream(manifestFile);
            try {
              ...
            }
            finally {
                in.close();
            }
        }
        catch (IOException ex) {
          ...
        }
В данном случае проблем не видно. Однако, допустим, проходит год-полтора, и кто-то расширяет ваш код:
        try {
            final FileInputStream in = new FileInputStream(manifestFile);
            final SomethingElse obj = SomeOtherStuff.create();
            try {
              ...
            }
            finally {
                in.close();
            }
        }
        catch (IOException ex) {
          ...
        }
И, если при вызове SomeOtherStuff.create() вылетает исключение, никто не закроет ваш in, т.к. close() выполняется в finally для другого try.

Тогда как если бы вы использовали try-with-resources, таких проблем у вас не было бы в принципе:

        try {
            try( final FileInputStream in = new FileInputStream(manifestFile) ) {
               final SomethingElse obj = SomeOtherStuff.create();
               try {
                 ...
               }
               finally {
                  in.close();
               }
            }
        }
        catch (IOException ex) {
          ...
        }

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

кто-то сам себе буратино. конструкция типа

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

это монолит. она должна (= обязана) выглядеть именно так - try сразу после resource = acquire();

на практике (проекты на той же Java 6) - во время code review несоответствие этому шаблону бросается в глаза. с try-with-resources возможностей ошибиться меньше, но это не более чем сахар к тому, что уже есть. важнее то, что там появилась возможность собирать suppressed exceptions, так что бросать исключения из finally или (неявно) из release фазы try-with-resources не приводит к утрате контекста возникновения ошибок.

а тот код должен собираться на Java 1.5, поэтому try-with-resources не используется. при этом важно то, что код без try-with-resources можно написать так же надёжно - есть простой шаблон.

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

А давайте я напомнювсем собравшимся сабж:

например, деструкторы, на которые жаловался кармак

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

Вообще тред начался с того, что раки, не могущие в C++ начали тупо лгать, причем с использованием тех же самых заходов вида «а если я буду писать через жопу».

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

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

это монолит. она должна (= обязана) выглядеть именно так - try сразу после resource = acquire();

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

В отличии от try-with-resources или C#-вского using.

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

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

я предлагаю прекратить кормить троллей. Просто предлагайте им никогда не писать на C++.

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

Никакими средствами языка эта монолитность не проверяется.

много чего не проверяется средствами языка/компилятора. а корректный и портабельный код писать надо.

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

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

великолепно справляется и без этого синтаксического сахара.
try-with-resources лучше чем этот шаблон для случаев безальтернативной очистки ресурсов.

facepalm.jpg

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

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

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

Ну хватит уже кормить троллей.

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

Вот человек требует завершить транзакцию в деструкторе и нигде больше. Что тут можно сказать? Да не надо ничего говорить, видно же что либо тролль либо дурак.

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

Тем кто пишет программы на C++ очевиден абсурд этих дебильных претензий в стиле «почему нельзя срать в штаны».

На C++ пишут программы очень разные люди. С очень разным уровнем знаний, с разным бэкграундом. Если человек до C++ программировал только на безопасных языках с GC, то у него вполне могут быть вот такие стереотипы в башке. А если еще и нет времени на то, чтобы читать нормальных авторов (вроде Страуструпа, Саттера, Мейерса и т.д.), то процесс приобретения нормального взгляда на C++ может затянуться надолго.

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

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

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

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

Да уж сильнее, чем ты набредил, не сделаешь.

Это не решит проблему

Никакой проблемы нет. Ты выдумал заведомо неправильный пример использования деструкторов, но у тебя оказался виноватым C++...

не сделает RAII в C++ на деструкторах универсальным

Ничего универсального нет. Даже твой лишп с макросами не универсален. :-)

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

Торвальдс придурок просто. Ибо он просто не знает либо не хочет знать как c++ транслируется внутри компилятора. Ядро макоси написано на c++ и работает

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

О вкусе колбасы можно спорить с теми, кто колбасу ест. Но спорить о вкусе колбасы с веганами... ну вы сами понимаете

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

Да уж сильнее, чем ты набредил, не сделаешь.

Причём тут я, если в цепепе нельзя из деструкторов исключения кидать? :-) Я что-ли виноват в этой ущербности, и что из-за этого нельзя сделать надёжного RAII на деструкторах? :-) Я всего лишь показал, что в цепепе так не сделаешь, с чего же ты вообще подумал, что я так делаю? :-) Всего то продемонстрировал любителям деструкторов, показав им всю «мощь» их любимого RAII в их любимом C++ на деструкторах в действии :-) Лол :-)

Никакой проблемы нет. Ты выдумал заведомо неправильный пример использования деструкторов, но у тебя оказался виноватым C++...

Никакой проблемы нет, кроме той, что из деструкторов цепепе нельзя ислючения кидать? :-) Я что-ли виноват в этом? :-) Нет, конечно, в этом C++ виноват :-) Точнее те, кто его придумали :-)

Ничего универсального нет. Даже твой лишп с макросами не универсален. :-)

По крайней мере в лишпе нет такой проблемы вообще :-) И убогих коллбэков, величаемых деструкторами, тоже, нет :-)

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

Не в курсе деталей. Главное что торвальдс лох

Никому не важно кто Торвальдс для тебя :-) Никому даже не важно кто Торвальдс в сравнении с тобой :-)

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

Я что-ли виноват в этой ущербности,

Вы виноваты в том, что называете это ущербностью.

и что из-за этого нельзя сделать надёжного RAII на деструкторах?

Покажите надежный RAII не на деструкторах.

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

Вот человек требует завершить транзакцию в деструкторе и нигде больше.

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

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

Вы виноваты в том, что называете это ущербностью.

А как это ещё называть? :-) Достоинством что-ли? :-) Виноват в том, что не фанатик? :-) Лол :-)

Покажите надежный RAII не на деструкторах.

Твоими же словами - с какой стати я кого-то должен учить за бесплатно? :-) Сам давай ищи надёжные RAII :-)

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

А как это ещё называть?

Это логичное следствие того, как происходит работа в C++.

Твоими же словами - с какой стати я кого-то должен учить за бесплатно?

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

Сам давай ищи надёжные RAII

Ну хоть критерии надежности-то сформулировать сможете? Или у вас смайликов не хватит?

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

Это логичное следствие того, как происходит работа в C++.

Ой, ты ж смотри-ка, логичное следствие он выискал в явном изъяне дизайна :-) Хахаха :-)

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

Хахаха :-) Ну вот, теперь у эксперта из Гомеля в записной книжке на один слив больше :-)

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

есть второй тезис «RAII - круто, универсально, надёжно.»

Интересно, где вы этот тезис увидели?

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

кстати, как вернуть ошибку из функции void f(void) noexcept?

Глобальная TLS-переменная, например. Правда зачем ставить такие требования.

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

А память на эти цепочки откуда брать?

Выделять место под один лишний указатель в процессе выделения памяти под объект исключения. Например.

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

Пока все предсказуемо. Критерии надежности можно не ждать?

Ты даже сам того не подозревая только что назвал главный критерий :-) Ещё подумай :-)

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

Глобальная TLS-переменная, например.

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

то, что предлагал выше asaw, тоже типа стэкового TLS.

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

Для C++ такое не пройдёт

Почему?

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

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

Создал свой класс от вектора, отнаследовался от своего же интерфейса сериализации в любой формат, реализовал нужные методы, вуаля!

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

Выделять место под один лишний указатель в процессе выделения памяти под объект исключения. Например.

Ну вот у вас идет раскрутка стека из-за bad_alloc. Под сам bad_alloc наличие памяти гарантируется. Но какой-то из деструкторов бросает исключение, под которое памяти нет. И...?

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

Ну вот у вас идет раскрутка стека из-за bad_alloc. Под сам bad_alloc наличие памяти гарантируется. Но какой-то из деструкторов бросает исключение, под которое памяти нет. И...?

bad_alloc - это сигнал об отсутствии свободной памяти :-) В этой ситуации по определению возможна потеря информации и вообще всё, что угодно :-) Так что не аргумент :-)

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

errno, что неприемлемо для большой кодовой базы.

Куда уж больше чем у errno.

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

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

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

Отлично, но исключения в текущем виде не все используют

Тогда цепочки их никак не затронут.

Опять же, как заставить правильно обрабатывать эти цепочки?

Ну меня бы устроило, если бы это были просто «вложенные» исключения относительно текущего(первого).

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

Ну вот у вас идет раскрутка стека из-за bad_alloc. Под сам bad_alloc наличие памяти гарантируется. Но какой-то из деструкторов бросает исключение, под которое памяти нет. И...?

В этой ситуации логично кинуть bad_alloc, но ведь он уже кинут, так что ничего делать не нужно.

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

Создал свой класс от вектора,

Зачем вам наследоваться? Вы же все равно к потрохам std::vector доступ не получите и будете вынуждены пользоваться только публичным интерфейсом.

А раз так, то вполне можно обходится без наследования и использовать тонкие обертки для сериализации, которые создаются только на момент сериализации/десериализации данных:

template< typename V >
class serializable_vector : public ISerializable {
  V & v_;
public :
  serializable_vector(V & v) : v_(v) {}
  virtual void Serialize(IArchive & to) override { ... }
  ...
};
...
std::vector< some_data > some_vector;
... // bla-bla-bla.
serializable_vector< decltype(some_vector) > s(some_vector);
s.Serialize(archive);

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

В этой ситуации логично кинуть bad_alloc, но ведь он уже кинут, так что ничего делать не нужно.

Ну т.е. терять инфу о каких-то проблемах — это нормально. Тогда какие претензии к текущему поведению в C++?

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

Ну т.е. терять инфу о каких-то проблемах — это нормально. Тогда какие претензии к текущему поведению в C++?

Терять информацию в условиях нехватки памяти - это нормально :-) А выставлять аргумент потери информации в условиях не хватки памяти - это наводит на мысли об отсутствии логики :-)

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

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

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

проверку результата очистки ресурса в «верхнем» объекте тоже надо как-то оборачивать так, чтобы она гарантированно была

«много чего не проверяется средствами языка/компилятора. а корректный и портабельный код писать надо» (с)

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

Ну т.е. терять инфу о каких-то проблемах — это нормально.

Когда нет памяти? Очевидно, да. bad_alloc - особый случай. Даже память под него резервируется, как вы сами сказали. Если память есть, то терять информацию бы не хотелось.

Тогда какие претензии к текущему поведению в C++?

В данном случае просто есть некоторая странность в дизайне языка. Для обработки ошибок предназначены исключения. Но в случае ошибок при разрушении объекта, этим механизмом пользоваться нельзя. Даже старыми «добрыми» кодами возврата нельзя пользоваться. Это, очевидно, изъян дизайна.

Как сделать «правильно» в C++ или в новом языке в тех же нишах? Точно не знаю. Но вариант с цепочками мне видится вполне разумным.

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

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

Очевидно, да.

А вот мне не очевидно. Есть исключение X, в процессе раскрутки стека возникает исключение Y. Вы хотите получить информацию и об X, и об Y.

Ok. Разумно. Такое желание может быть не только у вас.

Но выясняется, что бывают частные случаи, когда X=Y и вас вполне устраивает иметь только X.

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

Но момент с памятью — это еще цветочки. Про ягодки отдельным ответом.

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

bad_alloc изначально особый случай. Уже сейчас. Пока на аргумент не тянет.

Но выясняется, что бывают частные случаи, когда X=Y и вас вполне устраивает иметь только X.

Не частные, а частный. Когда у нас нет памяти для объекта-исключения.

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

Для обработки ошибок предназначены исключения.

Исключения прежде всего предназначены для обработки исключительных ситуаций, а не ошибок. Например, vector::at не может вернуть ссылку на несуществующий элемент, и тут ситуацию спасает исключение - опасный код просто не выполнится. А ошибки обрабатываются самыми разными способами.

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

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

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

Допустим, у нас есть объект A, который вносит сам себя в три глобальных списка L1, L2 и L3. Когда объект A разрушается, то он должен вычеркнуть себя из этих списков. Вероятно, он должен делать это в деструкторе и деструктор A будет выглядеть как-то так:

A::~A() {
  ... // (1)
  L1.remove(this); // (2)
  L2.remove(this);
  L3.remove(this);
  ... // (3)
}
Допустим, в точке (1) вылетает исключение. Это означает, что деструктор A прервет свою работу и действия в точках (2) и (3) не будут выполнены. А значит в списках L1, L2 и L3 останется ссылка на мусор, т.к. объекта A уже не существует.

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

К чему это нас приведет? К тому, что в одном месте throw приводит к одним последствиям, а в других местах — к другим? Как тогда быть, например, вот в этом случае:

void some_helper() {
  ... // bla-bla-bla
  if(some_condition_failed) throw some_exception();
  ... // bla-bla-bla
}
Ведь эта функция будет вести себя по разному: если ее вызвали внутри ~A, то исключение не будет прерывать ее работу. А если вне ~A, то будет.

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

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

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

Ну и по каким причинам это может не устроить какой-нибудь 1% пользователей языка? :-) Говорить об эффективности в момент раскрутки стека из-за исключений даже как-то стыдно :-) Так что если это аргумент, то он словно последняя соломинка утопающего :-)

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