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

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

В некоторых языках вообще исключений нет и живут же.

Из деструктора и «код возврата» не получить. Или глобальные(tls) переменные или сохранение в RAII-обертке ссылки/указателя на объект, в который можно передать ошибку.

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

Раскройте тему для данного случая.

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

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

Да, этот способ работает. Но это обработка ошибок через одно место.

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

В питоне

В питоне - совсем другая история, расход памяти и производительность. Там можно делать разные финты ушами, но за это приходится расплачиваться.

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

finally вы просто пишете код.

Ну вот напишите этот код.

Так же в той же Java «подавленные» исключения можно посмотреть.

Покажите. Желательно в каком-нибудь работающем коде. Это же распространенный случай, ведь так? Легко примеры найти должно быть.

В питоне в методе __exit__ вам доступно текущее исключение(в C++11 тоже, да) и даже остановить раскрутку.

Это от большого ума анонимы в данной теме упорно сравнивают С++ с языками с GC?

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

В питоне - совсем другая история, расход памяти и производительность. Там можно делать разные финты ушами, но за это приходится расплачиваться.

Раскройте тему применительно к данному случаю.

Например, C++:

struct A {
    A() { std::cout << "enter" << std::endl; }
    ~A() { std::cout << "exit" << std::endl; }
};
Аналогично в питоне:
class A():
    def __enter__(self):
        print("enter")
        return 1
    def __exit__(self, type, value, traceback):
        print("exit")



В отличие от C++, я смогу проигнорировать исключение, пропустить его далее или же выбросить свое собственное. Это более гибко и логично, чем C++ RAII

Чем бы за это пришлось поплатиться в C++? Текущее исключение можем не передавать(для этого в C++11 есть отдельные средства).
anonymous
()
Ответ на: комментарий от eao197

Это от большого ума анонимы в данной теме упорно сравнивают С++ с языками с GC?

При чем тут GC? Мы сейчас о закрытие ресурсов, не меняйте тему.

anonymous
()
Ответ на: комментарий от eao197
try (OutputStream stream = openOutputStream()) {
    // ...
}

Exception in thread "..." java.lang.RuntimeException: Main exception at ... at ... at ... Suppressed: java.lang.RuntimeException: Exception on close() at ... at ...

Suppressed видите?

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

При чем тут GC? Мы сейчас о закрытие ресурсов, не меняйте тему.

Я начинаю сомневаться, что вы пишите находясь в сознании:

и даже остановить раскрутку.

Ну расскажите, как в C++, например, при возврате из функции остановить раскрутку стека. В особенности когда разрушается объект с несколькими уровнями наследования, когда деструкторы из производных классов уже отработали, а в деструкторе одного из базовых ошибок происходи жопа.

eao197 ★★★★★
()
Ответ на: комментарий от eao197
try (OutputStream stream = openOutputStream()) {
    // ...
}
Exception in thread "..." java.lang.RuntimeException: Main exception
	at ...
	at ...
	at ...
	Suppressed: java.lang.RuntimeException: Exception on close()
		at ...
		at ...

Suppressed видите?

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

Еще раз: покажите, как finally решает проблему невозможности корректно закрыть файл. Если сможете, конечно.

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

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

Ну расскажите, как в C++, например, при возврате из функции остановить раскрутку стека

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

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

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

Зависит от задачи. Это все уже тут говорили. Например, мы уведомили пользователя и предложили ему пересохранить файл.

В случае отсутствия GUI отправили куда-то оповещение. Или банально записали в лог на том уровне, где у нас он есть. При этом сам класс для работы с файлом этого может не знать и спокойно работать над своей задачей.

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

Т.е. больше сравнивать C++ с языками с GC вы не будете?

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

Зачем вам это нужно, сможете рассказать? На конкретных примерах?

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

Зависит от задачи. Это все уже тут говорили. Например, мы уведомили пользователя и предложили ему пересохранить файл.

Я вас просил код показать. Без кода это все чепуха. А в коде вас легко тнуть в проблемы, которых в Java вы не замечаете, зато хотите избежать в C++.

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

Аналогично в питоне:

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

        mgr = (EXPR)
        exit = type(mgr).__exit__  # Not calling it yet
        value = type(mgr).__enter__(mgr)
        exc = True
        try:
            try:
                VAR = value  # Only if "as VAR" is present
                BLOCK
            except:
                # The exceptional case is handled here
                exc = False
                if not exit(mgr, *sys.exc_info()):
                    raise
                # The exception is swallowed if exit() returns true
        finally:
            # The normal and non-local-goto cases are handled here
            if exc:
                exit(mgr, None, None, None)

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

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

А если так?

template<typename T, typename F>
auto context_with(T && obj, F && f)
{
    context_enter(obj);

    struct g_ {
        g_(T *p) : obj_(p) {}
        ~g_() noexcept(false)
        {
            context_exit(*obj_, std::current_exception());
        }
        T * obj_;
    } g(&obj);

    return f();
}

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

Нет, надо заворачивать f() в try, как в реализации на питоне, а не использовать RAII, иначе будет та же проблема - context_exit кинет новое исключение и привет abort.

anonymous
()

Все таки я офигиваю, как люди могут путать транзакции и освобождение ресурса (ЛЮБОГО, даже файла, даже сети). RAII идеально ложиться на файлы. Кто отменял наличие метода close с исключениями, кодом ошибками и шлюхами? Никто. А вот ошибки в клиентском коде сплошь и рядом. Забыли поймать исключение, не продумали ветку и получай выход из функции с все еще открытым файлом. . А потом этот файл не откроешь из других кусков программы, или даже из других программ пока процесс жив. И что это хорошо?

Программисты люди, они делают ошибки. А допустить ошибку в 100500 функциях клиентского кода куда проще, чем в коде одного единственного библиотечного класса. Если добавить в библиотечный код, в деструктор насильное (с попыткой сохранения данных) освобождение ресурса, то многие фейлы просто исключаются. Да, в случае умных указателей и мьютексов RAII - это средство экономии кода и попытка избежать написания бессмысленного общего финализирующего блока, а так же перенос обязанности анлока/освобождения памяти на либу(на самом деле не только). Но очень часто освобождение ресурса в деструкторе - это защита от дурака. Если ты освободил ресурс в либе, то в коде юзающим твой класс критических ошибок будет меньше.

Почему люди пытаются ругать RAII за то что она не решает проблемы, которые и не должна решать? Деструкторы и НЕ должны завершать транзакции (На самом деле можно, если поизвращаться, но в общем случае нет).

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

Вряд ли. Логику «abort/retry/ignore» прямо в деструктор и поместить

Так в текстовом режиме она должна в консоль писать, а в графическом — окошко выбрасывать. Я с трудом представляю себе fstream, у которого в зависимостях WinAPI.

monk ★★★★★
()
Ответ на: комментарий от monk
template<class ErrorHandler>
class fstream
{
std::shared_ptr<ErrorHandler> h;
fstream() : h(new ErrorHandler{})
{
}
бла-бла
~fstream()
{
  try {
    бла-бла
  } catch (бла-бла) {
      h->ProcessError(бла-бла);
  }
  
}

Можно тоже самое сделать через интерфейсы без шаблонов, это уже вкусовщина.

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

Интересно как они считали.

Нашёл пример: http://citforum.ru/SE/project/arkhipenkov_lectures/13.shtml

Здесь нет Java, но есть C# с почти теми же характеристиками.

Строк на «задачу».

Язык    avg     min     max
C 	148 	9 	704
C++ 	60 	29 	178
C# 	59 	51 	66 

Обратите внимание на разброс max/min.

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

у которого в зависимостях WinAPI.

Как такую фразу вообще программист сказать может? Какие зависимости? А как же ООП, патерны, интерфейсы? Как же SOLID, где половина принципов косвенно намекает или прямо говорит: «создавайте зависимости от интерфейсов, а не от реализации».

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

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

2) Свободные функции не полиморфны, в отличие от функций членов.

class Test
{

};

class Test1 : public Test
{

};

void TestF(Test& t)
{
	std::cout << "Test" << std::endl;
}

void TestF(Test1& t)
{
	std::cout << "Test1" << std::endl;
}

int main()
{
	int a;
	Test t;
	TestF(t);

	Test* t1 = new Test1;
	TestF(*t1);

	delete t1;

	Test1 t2;
	TestF(t2);
	int i;
	std::cin >> i;

	return 0;
}
3) Тупой но полезный довод. Что бы узнать весь интерфейс в класса прямо в IDE(при условие, что имена/параметры методов ок). Мне достаточно написать obj. и увидеть выпадающий список. С свбодными функцими такое не кактит.

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

Dudraug ★★★★★
()
Ответ на: комментарий от Dudraug
Test* t1 = new Test1;
TestF(*t1);
delete t1;

На повестке дня стоит принятие C++17, а Dudraug, который рассказывает, как нужно программировать на C++, продолжает использовать голые new/delete. Сложно было написать так:

std::unique_ptr<Test> t1 = std::make_unique<Test1>();
TestF(*t1);
если уж приспичило создавать объект Test1 динамически.

Тем более в теме про RAII...

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

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

Для справки: std::auto_ptr был принят в C++98. На большинстве платформ он стал доступен где-то с 2002-го года (тормозила в этом деле, насколько помню, Visual Studio). За прошедшие годы необходимость использовать std::auto_ptr, а затем std::unique_ptr должна была бы войти в подкорку С++ников так же плотно, как и, например, запрет на возврат указателя/ссылки на временный объект. Такие вещи должны писаться просто на автомате, даже в игрушечных примерах.

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

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

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

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

По делу:

- сначала вы выразили желание наследоваться от std::vector для сериализации. Уже вопросы, ну да ладно;

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

- затем вы показали код, где вообще можно обойтись без динамической аллокации, где работаете через голые new/delete, и пазл сложился: человек пишет на C#, но в синтаксисе C++.

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

Ну попробуйте объяснить, с какого бодуна вам там потребовалось создать ErrorHandler через new. И хранить динамически созданный объект через shared_ptr, а не через unique_ptr.

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

По делу

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

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

Да мне то фиолетово. А вот вам уже несколько разных людей указали на то, что с идей наследования для сериализации в C++ вы не правы. И претензии ваши к свободным функциям необоснованны. Но вместо того, чтобы разобраться почему так, вам проще считать меня троллем. Хотя я только указал на самую вероятную причину таких ваших взглядов — недостаточный уровень владения C++.

Так что буду держаться. Вокруг слишком много C++неумех.

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

А вот вам уже несколько разных людей указали на то, что с идей наследования для сериализации в C++ вы не правы.

Как частный случай для только своих типов, с возможностью править все и вся, - вполне себе вариант. Если же говорить про общее решение, то, да, тут лучше взять, например, operator<< и operator>>.

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

идей наследования для сериализации в C++ вы не правы.

Было, но без аргументов. Только «можно и без этого». Но это, извини, не аргумент _против_. Это совет/указание как можно без этого. Но это не довод почему идея говно.

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

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

В идеале сериализация в xml( и желательно json) должна быть в стандартной либе. Если она там, то саободными функциями она сделанна, или мемберами уже неважно.

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

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

ЕМНИП, все началось именно с желания отнаследоваться от стандартных контейнеров для их сериализации в Ъ-ООП стиле.

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

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

недостаточный уровень владения C++.

Извини, но ты тролль. И ты и другие указывают на то что в с++ можно сделать так и так. Я с этим не спорил, где я оспаривал, что свободными функциями или внешними габлонными классами сериалищаторами это не сделать? Где я сказал, что что-то в с++ не сднлать? Я сказал о том, что стилистически(!!!) решения покрасивее(!!!) есть. Ты понимаешь, что предпочтение некой парадигмы не равно не знанию другой? Только школьник может этого не понимать. Или тролль ради троллинга отрицать. Ты на первого не похож.

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

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

Говори, что мое видение ооп говно(с аргументами), но не мешай сюда троллинг на знание языка

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

Я сказал о том, что стилистически(!!!) решения покрасивее(!!!) есть.

До появления STL в C++ пытались затащить разного рода библиотеки контейнеров, идеология которых была скопирована из других ОО-языков, где был единый корень иерархии наследования. От этого в тех библиотеках были свои базовые классы TObject, TList, TVector и т.д., тем или иным способом связанные иерархиями наследования. До сего времени из них если кто и дожил, то где-то на задворках. В отличии от STL-я, где подобной идеологии не было.

Так что вы пытаетесь наступить на грабли, обходить которые научились более 20 лет назад.

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

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

Ты себя слышишь? :-) Чуть более чем все C-программисты вызывают голый free(), и что? :-) Чуть более, чем каждый уважающий себя C-программист знает C++, и что, если он не использует смарт-поинтеры, то его «уровень подготовки недостаточен»? :-) Что за чушь? :-)

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

Да уж конечно, ага :-)

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

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

Это значит, что он и на С++ может писать на С.

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

Это значит, что он и на С++ может писать на С.

И что? :-) Многие так и пишут, используя лишь часть фич из C++ :-) Об уровне подготовки судят по результату, а не по наличию/отсутствию смарт-поинтеров в коде :-)

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

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

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

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

Речь не о сложности использования delete/free(), а о стереотипах писать в стиле «современного C++», непременно используя смарт-поинтеры и всю ту вереницу фич и паттернов, о которых любезно рассказывают всякие эксперты в своих сборниках рецептов написания Ъ-кода :-) Ниже я сказал, что многие используют лишь часть фич из цепепе, в т.ч., исключения рассматриваются как опция :-) Ну а дальше ты понял :-) Простое присутствие new/delete/malloc/free в коде цепепе ещё не говорит о том, что код плохой, а его автор - не подготовленных ламерок :-) Говорит лишь о том, что код не соответствует каким-либо гайд-лайнам/книжкам/рецептам, что у ценителей «настоящего» сурового кода на цепепе может вызывать недоверие или отторжение :-)

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

RAII идеально ложиться на файлы. Кто отменял наличие метода close с исключениями, кодом ошибками и шлюхами? Никто.

Объясняю. В Си ты должен вызвать close. Ты это сделаешь явно. И будет видно, что ты игноришь ошибку(если игноришь). В C++ ты привыкаешь, что ресурсы закрывает RAII и ручное закрытие является дурным тоном. В результате проверок в плюсовом коде еще меньше. Люди просто забивают. Или логируют критичные вещи в деструкторах, давя все исключения.

Тут явно что-то не так. Но предложить мне лично нечего...

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

Смайлик, ты лучше расскажи о том, как правильно работать с ресурсами... Через (with-* ...)? Это тоже RAII, вид сбоку. И часто ли обрабатывают ошибки закрытия в этом варианте?

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

Тут явно что-то не так. Но предложить мне лично нечего...

Предложи себе: использовать C++ так, как удобно лично тебе, или приспосабливаться к каким-либо гайд-лайнам, или не писать на C++ :-)

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

Так же интересно, как «правильно» обрабатывать ошибки, возникающие в процессе обработок ошибок

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

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

Правильно - это значит в соответствии с правилами :-) А правила у каждого свои :-)

Через (with-* ...)? Это тоже RAII, вид сбоку.

Это RAII, но в Лиспе сигнал об ошибке не раскручивает стек, со всеми вытекающими из этого следствиями :-) Обработчик может отреагировать по-разному, выбрав стратегию восстановления (рестарт) исходя из текущего контекста :-)

И часто ли обрабатывают ошибки закрытия в этом варианте?

У каждого свои правила :-)

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

на C нет исключений, на C++ - есть. поэтому на C++ free() вызывать корректно тяжелее.

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

static jmp_buf buf;

void second(void) {
    longjmp(buf,1);
}

void first(void) {
    void* p = malloc( 1024 );    
    second();
    
    printf("free :(\n");
    free(p);
}

int main() {   
    if (!setjmp(buf))
        first();

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

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

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

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