-
Тут аналогия пришла в голову.
Разица между finally и автоматическими деструкторами примерно такая же как между конструкциями goto и repeat..until/do..while/for.
Первые (finally и goto) проще, при этом имеют большие возможности.
Вторые (RAII и конструкции для циклов) делают код более читабельным и навязывают правильную структуру программы.
-
Не совсем. Не зря же в MSVC есть __finally?
Во-первых, goto по сравнению с циклами не имеет больших возможностей. Любую программу, с использованием goto можно переписать с использованием циклов. Циклы предоставляют удобный общепонимаемый шаблон. Но опять же, существуют задачи, которые этими шаблонами решать, мягко говоря, неудобно.
Автоматические деструкторы и try/finally взаимозаменяемы. Недостатки каждой конструкции очевидны: try/finally заставляет нас всякий раз повторять однотипный код в секции finally. Автоматические деструкторы заставляют городить нам всякий раз обвертку в виде класса, даже если действие уникально.
-
> Не зря же в MSVC есть __finally?
Он для SEH только. В таком виде он чтобы работал и в С без ++. Совместно с деструкторами он не работает. В реальном коде на С++ его не встречал. В более портабельных и соотвествующих стандарту компиляторах finally нет.
С остальным, всё так, но "недостаток" finally намного меньший недостаток, чем требование писать однотипный код, требование читать однотипный код, и возможность сделать в этом однотипном коде ошибки.
Для "разового" когда можно использовать локальные классы, тогда код освобождения будет как и с finally там где и выделение, и в отличии от finally, это освобождение будет рядом с местом выделения, а не в конце блока.
-
Я не вижу такой уж большой разницы в этом. Такой вариант мне даже более приятен
hFile := CreateFile(...);
try
...
finally
CloseHandle(hFile);
end; чем
class win_handle_t
~win_handle_t()
private:
HANDLE handle;
};
win_handle_t hFile = CreateFile(...); Потому что второй вариант не исчерпывает все возможные операции с дескрипторами. Например, есть еще копирование, присваивание и т.п. Постепенно класс win_handle_t будет разрастаться, при этом размер каждого конкретного метода будет не более чем пару строк. В мозгу у меня ограниченное чисто ячеек памяти, моя практика показывает, что забыть о внутренностях этого класса у меня не получается. Лично мне предпочтительнее когда все выполняется по месту.
-
> Mystic © (27.10.08 15:13) [3]
Разница тут в головах, в стиле написания программ :) Кроме того, RAII даёт больше, чем отказ от finally. Например, возможность иметь объекты-обёртки членами классов серьёзно упрощает написание классов, "держащих" какие-то ресурсы, будь то динамически выделяемые буферы или объекты операционной системы. Другой пример - обращение с примитивами синхронизации. Это действительно удобно.
Я, кстати, много программировал и на Дельфи и на С++, так что имею возможность сравнивать.
-
RAII, RAII в Delphi. А как же способы прикрутить семантику копирования? 1. Реализовать интерфейс и работать с ним. Или просто задействовать трюк. MyClassHelper=class helper for TInterfacedObject function HiddenRef:IUnknown;inline; end; { MyClassHelper } function MyClassHelper.HiddenRef: IUnknown; begin result:=Self; end; procedure TForm2.Button1Click(Sender: TObject); var a:TsomeDerivedFromTInterfacedObject; begin with TsomeDerivedFromTInterfacedObject.Create do begin HiddenRef; //Do something end; end; 2. Прикрутить семантику сбоку. http://barrkel.blogspot.com/2008/09/smart-pointers-in-delphi.html
-
> Любую программу, с использованием goto можно переписать > с использованием циклов.
Каждый овощ ... (с) Игорь Шевченко
Вот у меня недавно получился такой код for ... case ... of 1 a: 2 3 if ... then goto a
Смысл в том, что конечный автомат, и в ряде случаев после 3 надо сделать 1. Да, я в курсе, что можно сотворить отдельную процедуру для этого 1. Но это не улучшает читабельность (отдельная процедура на 4 строчки). Да, я знаю, что можно скопипастить код из 1 в 3. Но это не улучшает отладку ошибок, и ту же читабельность. Да, я понимаю, что можно наплодить стейтов, но тут уж про понябельность придеться забыть совсем. И я также в курсе, что любой нормальный программист сумеет разорбраться в коде из 10-15 строчек, даже если там есть один goto. нне развалится. Тем более, что разбираться кроме меня вообще никто не будет. Особенно, если он привык к break, continue, exit, raise.
Правда, справедливости ради, сделать хотелось срочно, и на архитектуру я, к сожалению, забил. Знаю, что низя. Но надо было.
-
> что разбираться кроме меня вообще никто не будет
в таком случае неважно как ты пишешь.
-
> [7] jack128_ (27.10.08 22:41) >> что разбираться кроме меня вообще никто не будет
> в таком случае неважно как ты пишешь.
А себя не жалко, когда через пару месяцев будешь разбирать свои же исходники ? :)
-
Mystic © (27.10.08 15:13) [3]
> Например, есть еще копирование, присваивание и т.п
Не хочешь - не реализуй, а запрети. Одна строчка - наследование от noncopyable - и уже некопируемое владение, которое при этом более широко применимо чем finally. Для владения мьютексом запрет копирования - самое разумное решение.
Alkid (27.10.08 16:58) [4] +1
oxffff © (27.10.08 19:57) [5] Хакер :) Хотя вот в TXMLDocument тоже такое предлагают When TXMLDocument is created without an Owner, it behaves like an interfaced object. That is, when all references to its interface are released, the TXMLDocument instance is automatically freed.
TUser © (27.10.08 20:33) [6] Всё правильно. goto иногда бывает к месту, и без него лишнее писать приходится. Именно это я имел ввиду под "большими возможностями goto". Ключевой момент здесь "иногда". Настолько редко, что случаи можно запомнить. Точно так же "иногда" finally уместнее RAII. Кстати, в С/С++ case допускает проваливания, но несмотря на возможную полезность этой фичи (Duff's device :) от неё больше вреда чем пользы.
-
> Riply © (27.10.08 23:03) [8] > > > [7] jack128_ (27.10.08 22:41) > >> что разбираться кроме меня вообще никто не будет > > > в таком случае неважно как ты пишешь. > > А себя не жалко, когда через пару месяцев будешь разбирать > свои же исходники ? > :)
+1
-
> Не хочешь - не реализуй, а запрети. Одна строчка - наследование > от noncopyable - и уже некопируемое владение, которое при > этом более широко применимо чем finally.
Это сужает область работу с дескриптором. Например, я могу спокойно хранить дескрипторы полях Tag, Data или внутри TList. Посылать его в wParam или lParam. Передавать как параметр в ThreadFunc. Еще могут быть интерфейсы и методы, которые принимают LPHANDLE.
Есть еще и то соображение, что нельзя рассматривать фичи отдельно от окружения. У Delphi свой стиль, свои приемы. У C++ свои. Если в Delphi нормальная практика хранить в полях Tag, Data указатели на объекты, то в случае C++ применяют другие подходы. Т. е. затрагивая один аспект мы задеваем одну костяшку домино, которая затрагивает другие.
-
> guav © (27.10.08 23:27) [9] > oxffff © (27.10.08 19:57) [5] > Хакер :) > Хотя вот в TXMLDocument тоже такое предлагают > When TXMLDocument is created without an Owner, it behaves > like an interfaced object. That is, when all references > to its interface are released, the TXMLDocument instance > is automatically freed.
В TXMLDocument просто предлагают предлагать менять семантику копирования при создании компонента. Однако к наделению самой семантикой копирования это не имеет отношения.
В С++ автоматический вызов декструктора приводит к тут же к необходимости
-перегрузке оператора присваивания. -созданию конструктора копий.
По причине того, что другие базовые типы "прямолинейны" (лишены семантики копирования). Это строки, интерфейсы, массивы, варианты.
В Delphi необходимости в автоматическом десктрукторе нет, поскольку всегда предлагается деструтор(финализатор) по умолчанию. И строки, интерфейсы, массивы, варианты более гибкие. Так что RAII в Delphi был с рождения.
P.S. Это не камень в огород С++. Каждый подход имеет свои плюсы и минусы.
-
Mystic © (28.10.08 02:09) [11]
> Это сужает область работу с дескриптором. Например, я могу > спокойно хранить дескрипторы полях Tag, Data или внутри > TList. Посылать его в wParam или lParam. Передавать как > параметр в ThreadFunc. Еще могут быть интерфейсы и методы, > которые принимают LPHANDLE.
В текущей версии С++ для хранения внутри list и передачи в ThreadFunc прийдётся реализовать "честное" копирование или использовать враппер типа shared_ptr. В новой версии стандарта добавятся rValue ссылки и std::move - можно будет помещать в контейнеры классы, не поддеживающие копирования, досаточночно будет поддержки перемещения.
Посылать в wParam или lParam - надо просто открыть вклассе доступ к самому дескриптору, сделав поле public, или реализовав метод get или оператор приведения к дескриптору. Помещение в Tag, Data это не путь С++, но если очень хочется, то также как и посылать в wParam или lParam.
> Есть еще и то соображение, что нельзя рассматривать фичи > отдельно от окружения. У Delphi свой стиль, свои приемы. > У C++ свои. Если в Delphi нормальная практика хранить в > полях Tag, Data указатели на объекты, то в случае C++ применяют > другие подходы. Т. е. затрагивая один аспект мы задеваем > одну костяшку домино, которая затрагивает другие.
Да, это так. Я и не предлагаю использовать RAII в Delphi. я вообще ничего не предлагаю, просто поделился впечатлением после некоторого опыта в С++ : RAII работает лучше finally, и finally в C++ не стали делать не зря.
oxffff © (28.10.08 09:10) [12]
> Однако к наделению самой семантикой копирования это не имеет > отношения.
На самом деле, в Delphi все указанные типы с автоматической финализацией наделены семантикой копирования - это подсчёт ссылок (кроме для Variant может быть и копирование по значению в зависимости от содержимого).
Есть ещё типы с запретом копирования. Это File.
> И строки, интерфейсы, массивы, варианты более гибкие.
В Delphi они менее гибкие, за счёт "встраивание магии в компилятор", что делает их более удобными, но к RAII это не относится.
> Так что RAII в Delphi был с рождения.
Был. Но не для наследников TObject. Почему так сделано ?
-
> guav © (28.10.08 10:05) [13]
> oxffff © (28.10.08 09:10) [12] > > > Однако к наделению самой семантикой копирования это не > имеет > > отношения. > > На самом деле, в Delphi все указанные типы с автоматической > финализацией наделены семантикой копирования - это подсчёт > ссылок (кроме для Variant может быть и копирование по значению > в зависимости от содержимого). >
Я тебе про Фому, а ты мне про Ерему. :)
Твой пост.
> Хотя вот в TXMLDocument тоже такое предлагают > When TXMLDocument is created without an Owner, it behaves > like an interfaced object. That is, when all references > to its interface are released, the TXMLDocument instance > is automatically freed.
у меня вопрос какое отношение имеет Owner, к процессу наделения семантикой копирования? Никакого. Зато он имеет отношение к реализации смысла самой самантики копирования На что я тебе и указал. По твоим словам получается что owner каким то образом заставляет компилятор вставлять _НаВасЕстьСсылка, _ОтВасотказались.
> И строки, интерфейсы, массивы, варианты более гибкие.
> В Delphi они менее гибкие, за счёт "встраивание магии в > компилятор", что делает их более удобными, но к RAII это > не относится.
Очень интересно. А что в С++ для class и struct автоматические вызовы деструктора, оператора присваивания, конструктора копий происходят сами сабой, без участия компилятора? Не подскажешь как?
> > Так что RAII в Delphi был с рождения. > > Был. Но не для наследников TObject. Почему так сделано ? >
А ты что не в курсе? :) Что в Delphi объекты размещены не стеке, а в куче(по умолчанию). А для копирования объеков кучи у С++ и Delphi семантика копирования идентичная. Копируется указатель. Это так называемая идентичность, но не эквивалентность. Все остальная семантика копирования объектов кучи , оборачивается в С++ за счет стековых классов оберток. Чую holywar назревает.
-
> oxffff © (28.10.08 09:10) [12] > Так что RAII в Delphi был с рождения.
Не так. Правильно так - RAII изначально реализован в частном видел для ряда типов. Реализация его для других типов требует э-э-э-.... стратегии НЕПРЯМЫХ действий (т.е. через Ж) :)
Это мне напоминает дизайн языка D, где они попытались для каждого частного случая внедрить прямую поддержку языка. В С++ RAII (как и многое другое) получилось как комбинация нескольких базовых механизмов языка, по сути ортогональных друг другу.
ИМХО, попытка внедрять прямую поддержку "фитч" на все случаи жизни - тупиковый пусть развития языка. Правильнее разрабатывать мощный базис из нескольких фундаментальных механизмов с высокой степенью ортогональности и возможностью их комбинировать. Ну это так, оффтопик в сторону дизайна языков :)
-
> Alkid (28.10.08 10:57) [15]
А что уже при копировании указателя на объект в С++ используется семантика копирования? Почему прикручивание smart pointer в C++ также не назвать через Ж? Прошу ответить честно. Уж не политика ли это двойных стандартов? :)
-
> Очень интересно. А что в С++ для class и struct автоматические > вызовы деструктора, оператора присваивания, конструктора > копий происходят сами сабой, без участия компилятора? Не > подскажешь как?
Я имел в виду встраивание в компилятор поддержки для IUnknown, поддержки для сообщений Windows, поддержки IDispatch при невозможности дать пользователю реализовать нужную семантику. Т.е.
> [15] Alkid (28.10.08 10:57) > попытались для каждого частного случая внедрить прямую поддержку языка.
вместо введения
> [15] Alkid (28.10.08 10:57) > нескольких базовых механизмов языка
-
> oxffff © (28.10.08 11:13) [16]
Собственно говоря, разница тут такая: в С++ изначально определялся ряд фундаментальных механизмов. В частности, в рамках нашего спора, нас интересуют следующие: 1. Шаблоны. 2. Пользовательские типы с возможностью создавать статические, динамические и автоматические экземпляры, а так же непосредственно включать их как поля других пользовательских типов.
Каждый из этих встроенных в язык механизмов по отдельности не даёт RAII, смартпоинтеров и т.п. Каждый из них - это механизм общего назначения, но их комбинация породила ряд специализированных мезанизмов, в частности RAII со смартпоинтерами.
В Дельфи части этих механизмов нет. В частности нет возможности создавать объекты классов кроме как в куче (динамические объёкты). Так же нет шаблонов, правда в .NET-версии Дельфи появились дженерики.
Что это означает? Та комбинация общих механизмов, которая в С++ давала возможность реализовать RAII, в дельфи не реализуема. Что бы исправить эту ситуацию, в самом языке Дельфи начинают реализовывать ПРЯМУЮ поддержку этих специальных механизмов (RAII) для некоторых избранных типов, оставляя "за бортом" пользовательские типы.
Что остаётся делать программистам, что бы обойти эту неприятную ситуацию с пользовательским типами? Им приходится использовать ещё один специализированный механизм, предназначенный для чего-то другого. А именно - ПРЯМУЮ поддержу технологии COM языком Дельфи. Т.е. использовать УЗКОСПЕЦИАЛИЗИРОВАННЫЙ механизм за пределом его специализации. Это и есть "через Ж", если ты хочешь строгого определения :)
Касаемо двойных стандартов, холиваров и т.п: хочу пояснить свою позицию по поводу С++ и Дельфи. Я считаю, что оба языка серьёзно устарели. С++ остался слишком низкоуровневым. Тяжкое наследие С, которое они упорно тащат за собой, делает его монстроидальным и переусложнённым, а жёсткая дисциплина модифицирования стандарта делает язык отстающим от реалий нашего дня. С другой стороны, в Дельфи проглядывается отсутствие вообще какой-либо дисциплины нововведений, что так же делает его монстроидальным и весь его дизайн прекрасно описывается простой латинской фразой - "ad hoc".
Я считаю, что оба языка исчерпали себя. Дельфи исчерпал себя в бОльшей степени просто в силу готовности конкурирующих языков занять его место. Хорошей замены для С++ в тех нишах, где он держит свои позиции просто нет. Это печально.
-
> guav © (28.10.08 10:05) [13]Все проблемы решаемы, но я просто хотел показать, что они есть. Ну и перечислил первое, что пришло в голову. Тем более, что надо реализовывать не get(), а release(). И это один из самых простых примеров. И новый стандарт покуда не везде реализован, правда? Вторым минусом является то, что для того, чтобы понять такой код, надо знать не только WinAPI, но и как минимум просмотреть реализацию класса-обвертки. Я не думаю, что этот класс сохранится в таком минимальном виде, скорее всего в него будут добавляться алиасы для методов вроде DuplicateHandle. Что приводит к необходимости потом мучительно вспоминать, что реализовано в этом классе, а что нет. Так что небо не такое уж и безоблачное, лично я при программировании на С++ стараюсь таких обверток избегать. Если надо активно использовать WinAPI, или какую-нить C-шную библиотеку, то этот фрагмент я пишу в C-подобном стиле, а потом оборачиваю его в некоторый класс. Такой уж мой вкус. В крайнем случае try..finally легко реализовать в C++ и через
try
catch(...);
с учетом небольшой потребность в нем из-за автоматических деструкторов, вводить специальное слово абсолютно не было нужды.
|