Delphi vs C++ vs C#
, ну и Java немножко, где-то ближе к старшему байту номеров страниц
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.192] |
|
|
Правила раздела:
| Страницы: (495) « Первая ... 478 479 [480] 481 482 ... 494 495 ( Перейти к последнему сообщению ) |
Delphi vs C++ vs C#
, ну и Java немножко, где-то ближе к старшему байту номеров страниц
|
Сообщ.
#7186
,
|
|
|
|
Что интересно, деструктор не требуется даже в таком варианте: ![]() ![]() #include <iostream> #include <memory> class InnerClass { public: InnerClass() {std::cout << "InnerClass::InnerClass" << std::endl;} ~InnerClass() {std::cout << "InnerClass::~InnerClass" << std::endl;} }; class OuterClass { public: OuterClass() { m_inner.reset(new InnerClass()); throw 10; } private: std::unique_ptr<InnerClass> m_inner; }; int main() { try { OuterClass cls; } catch (...) { std::cout << "Exception thrown" << std::endl; } } |
|
Сообщ.
#7187
,
|
|
|
|
Не, нормально на самом деле. Явного указания требуют только POD - сишечное наследие. |
|
Сообщ.
#7188
,
|
|
|
|
Цитата MyNameIsIgor @ Не, нормально на самом деле. Явного указания требуют только POD - сишечное наследие. В С++11 ситуацию облегчили standard layout-типами. |
|
Сообщ.
#7189
,
|
|
|
|
Цитата trainer @ А, ну да. Забыл приписать "... или положить на одну из моделей и юзать только (по возможности) вторую." У меня никакого алеса не было. Родного с Delphi/VCL был только редактор форм и реакция на события компонента. AfterConstruction/BeforeDestruction использовал только один раз, в качестве костыля уже не помню к чему. Добавлено Вот, буквально на днях читал очередную лекцию на работе. Примеры оттуда. ![]() ![]() /* Небезопасный код */ class A { int *x; int *y; public: A(int a, int b): x(new int(a)), y(new int(b)) {} /* утекает x, если второй new провалится */ A(const A& z) : x(new int(z.x)), y(new int(z.y)) {} /* утекает x, если второй new провалится */ A& operator=(const A& z) { if (this == &z) return *this; /* наивная попытка обеспечить безопасность коду */ delete x; /* в случае сбоев мы теряем */ delete y; /* старое состояние объекта */ x = new int(z.x); y = new int(z.y); /* утекает x, если второй new провалится */ return *this; } ~A() { delete x; delete y; } }; ![]() ![]() /* Строгая гарантия соблюдается. Лобовое решение, где класс по-прежнему владеет обоими указателями. Цель достигнута, но код сложен для поддержки и дальнейшего развития. */ #include <new> class A { int *x; int *y; public: A(int a, int b): x(new int(a)) /* здесь пока нет изменений в окружении */ { try { y = new int(b); /* а здесь уже есть, поэтому нужно позаботиться об x */ } catch(std::bad_alloc) { delete x; throw; /* исключение НЕ обрабатывается в этом месте, поэтому передаём его дальше */ } } A(const A& z) : x(new int(z.x)) /* здесь пока нет изменений в окружении */ { try { y = new int(z.y); /* а здесь уже есть, поэтому нужно позаботиться об x */ } catch(std::bad_alloc) { delete x; throw; /* исключение НЕ обрабатывается в этом месте, поэтому передаём его дальше */ } } A& operator=(const A& z) { /* сначала полностью подготовим новое состояние */ int *tmp1 = new int(z.x), /* здесь пока нет изменений в окружении */ *tmp2 = 0; try { tmp2 = new int(z.y); /* а здесь уже есть, поэтому нужно позаботиться о tmp1 */ } catch(std::bad_alloc) { delete tmp1; throw; /* исключение НЕ обрабатывается в этом месте, поэтому передаём его дальше */ } /* теперь, когда новое состояние полностью готово, применим его к объекту */ /* все четыре операции отказобезопасны */ delete x; delete y; x = tmp1; y = tmp2; return *this; } ~A() throw() { delete x; delete y; } }; ![]() ![]() /* Строгая гарантия соблюдается. Задача управления двумя указателями декомпозирована до двух задач управления по одному указателю. Класс разбит на два, каждый класс владеет только одним указателем, и один из них сделан базовым для второго. Цель достигнута, причём меньшим количеством кода и очень изящно, без try/catch. */ #include <new> #include <algorithm> /* Вспомогательный класс для одного из указателей */ class A_base { int *x; /* класс не предназначен для использования отдельно от второго, нужды в public-интерфейсе нет */ protected: A(int a) : x(new int(a)) {} /* строго безопасно */ A(const A& z): x(new int(z.x)) {} /* строго безопасно */ A& operator=(const A& z) /* строго безопасно */ { int *tmp = new int(z.x); /* сначала подготовим новое состояние, */ /* а теперь применим его */ delete x; /* безотказно */ x = tmp; /* безотказно */ } /* безотказно */ ~A() throw() { delete x; } /* вспомогательный метод для обмена состояниями двух объектов класса A_base безотказно, т.к. std::swap() безотказен для POD-типов, в частности указателей */ void swap(A_base& z) throw() { std::swap(x, z.x); } }; /* Основной класс для второго указателя, первый обслуживается базовым классом. Деление на два класса тут является деталью реализации, поэтому наследование приватно. */ class A: private A_base { int *y; public: A(int a, int b): A_base(a), y(new int(b)) {} /* строго безопасно; если new провалится, */ A(const A& z) : A_base(z.x), y(new int(z.y)) {} /* базовый класс сам за себя ответит */ A& operator=(const A& z) /* строго безопасно */ { /* сначала подготовим новое состояние */ A_base tmp1(z.x); /* объект базового класса, он освободит x в случае провала new */ int *tmp2 = new int(z.y); /* новый указатель для y, в случае провала за х ответственен tmp1 */ /* а теперь применим его */ tmp1.swap(*this); /* теперь наш базовый x во временном объекте, а новый x у нас */ std::swap(y, z.y); /* аналогично наш указатель y */ delete tmp2; /* удаляем старый y */ } /* а тут удаляется временный объект с теперь уже старым x */ ~A() throw() { delete y; } /* безотказно */ /* последующий контент не требуется для примера, но желателен для рабочего приложения */ /* безотказно */ void swap(A& z) throw() { z.A_base::swap(*this); std::swap(y, z.y); } }; /* этот контент - тоже последующий */ namespace std { /* Делегируем специализации std::swap() для нашего класса его методу. Для A_base специализация не требуется, ибо вне A он всё равно недоступен, а для A она не нужна */ template<> void swap(A& x, A& y) { x.swap(y); } } |
|
Сообщ.
#7190
,
|
|
|
|
Цитата Во-первых, дефолтная инициализация нулями по любому лучше (по кр.мере предсказуеемее), чем случайный мусор. Я не знаю, измерял ли кто нибудь это на последних версиях компилятора. Наши измерения показывали для VS 2005Ж явная инициализация нулями для достаточно простых объектов (типа, многоугольник) давала проигрыш примерно 25% по сравнению с инициализацией мусором. Неожиданно, внезапно, и неприятно, когда тебе этих объектов нужно создать сотни тысяч. |
|
Сообщ.
#7191
,
|
|
|
|
Цитата D_KEY @ Сначала зануляете поля, потом что-то меняете. У нас же сразу вызываются конструкторы полей(а те вызывают конструкторы своих полей и т.д.)... Если слово "сразу" - это намек на некую скорость из-за отсутствия якобы лишних действий - то это заблуждение, т.к. единый memset (на современных компах) выполняется намного быстрее, чем выборочная дефолтная или зеро-инициализация каждого мембера в отдельности. В дельфях тоже есть функция Initialize() для выборочной зеро-инициализации auto-managed типов, но на практике вместо нее чаще используется сплошное зануление всей структуры или массива FillChar'ом (аналогом memset) - так проще и быстрее. А если "дело принципа", то ничего криминального в предварительном обнулении структуры нет, т.к.сплошные нули - это просто частный случай того же мусора И если приплюснутые компиляторы этого не делают, будучи ограничены рамками "бородатых" традиций, закрепленных стандартами, то это не означает, что такой подход не годится для других языков и моделей ООП ![]() Отвечу твоими же словами: Цитата D_KEY @ Ага, создали проблему и подставили костылик. Не надо вызывать деструкторы для несозданных объектов и все будет хорошо Типа "забыли", что объект может содержать не только subobject-ы целиком, но и ссылки\указатели на динамически выделенные классы\структуры\массивы. Ах, ну вот вам тогда костылик - заворачиваем любой указатель в фантик-класс под названием auto\unique_ptr - и вуа-ля, теперь он становится обычным subobject и по стандарту должен быть уничтожен вызовом собственного деструктора (если успел создаться до выброса исключения). Очень мило ![]() ================= И вообще, в дельфи и С++ модели ООП "концептуально" разные, по крайней мере в плане конструкции\деструкции. В дельфи вообще нет такого понятия как subobject-ы, соотв-но нет и никакой жестко предопределенной иерархии автовызова конструкторов и деструкторов, и соотв-но нет понятий "сначала создали это, потом это" или "это уже создано, а это еще нет". Все создание объекта отдается на откуп программисту как "творцу\создателю" иерархии классов: хочешь\нужно вызвать конструктор родительского класса в дочернем конструкторе - вызывай, можно сразу перед своими действиями, а можно и после или между, или можно вообще ничего не вызывать и проинициализровать все по своему "с чистого листа" (если есть возможность, ес-но). Аналогично и с деструкторами - если сам не организуешь цепочку вызовов, то никто за тебя это делать не будет, а в случае фолта в конструкторе автоматом вызывается лишь деструктор самого создаваемого класса, который сам "по косвенным признакам" должен определить, что уже создано и должно быть удалено, а что нет. "Худо-бедно", но предварительное обнуление всех полей в InitInstance решает эту проблему на 99.(9)% (а "если где-то кое-где у нас порой" встречаются принципиально ненулевые инициализаторы типа INVALID_FILE_HANDLE, то к ним можно и дополнительный флаг-костылик приставить - "в семье не без урода" ).Зато имеется возможность введения полиморфизма в сам процесс конструирования объекта, в том числе и за счет использования виртуальных методов. А это как-то погибче и ближе к природе, нежели только "тупо копировать" своих предков Интересно, в С++ модели можно сделать "из обезьяны человека", или нужно непременно сначала целиком создать обезьяну и только затем "приводить ее в человеческий вид", повторяя весь эволюционный ход истории? В дельфийской модели в принципе можно, если "природа"\"творец" заложили в конструктор обезьяны возможность учета "виртуальных мутаций" непосредственно в процессе конструирования ![]() PS: Разумеется все эти "зато" не могут служить аргументами в предвзятом споре в стиле "А из нашего окна площадь Красная видна, а из вашего окошка - только улица немножко!!!!" |
|
Сообщ.
#7192
,
|
|
|
|
Цитата leo @ Это костылик?? Побойся бога. Сырые указатели давно пора выпилить из плюсов вообще нафик. Только этого никогда не будет. Ты б ещё шаблоны костыликом к макросам обозвал. Ах, ну вот вам тогда костылик ... Добавлено По ходу leo решил по ... э-э-э, подскажите, какому разу всё мочалить сначала. |
|
Сообщ.
#7193
,
|
|
|
|
Цитата Бобёр @ явная инициализация нулями для достаточно простых объектов (типа, многоугольник) давала проигрыш примерно 25% по сравнению с инициализацией мусором Если речь о простых объектах, которые могут вообще не инициализироваться при создании (или инициализироваться частично), то ес-но их обнуление - это лишняя трата времени. Но в общем случае, когда приходится "думать", что нужно инициализировать, а что нет, тупое обнуление часто работает быстрее. Цитата Qraizer @ Это костылик?? Побойся бога Это обмен любезностями с теми, кто видит массу костылей в чужих огородах, но не замечает в своих ![]() Это костыль не сам по себе, а в сочетании с принципом не вызова деструктора "еще несозданного объекта" (по вашей терминологии), который просто вынуждает "порядочных" людей оборачивать указатели фантиками=костыликами (ну или городить огороды с try\catch, декомпозицией и т.п. как в твоих последних примерах) Цитата Qraizer @ Сырые указатели давно пора выпилить из плюсов вообще нафик D_KEY еще и деструкторами не пользуется - их тоже выкинуть? Добавлено Цитата Qraizer @ По ходу leo решил по ... э-э-э, подскажите, какому разу всё мочалить сначала Первый раз заглянул в этот раздел, и слово вымолвить нельзя? Неужели ты думаешь, что кто-то из "заглянувших на огонек" будет штудировать все 480 страниц - от силы пяток-другой последних. Соотв-но, подобный холивар по природе своей бесконечен - "шыло, мочало, все начнем с начала" Шутка - меня надолго не хватит |
|
Сообщ.
#7194
,
|
|
|
|
Цитата leo @ Если слово "сразу" - это намек на некую скорость из-за отсутствия якобы лишних действий - то это заблуждение, т.к. единый memset (на современных компах) выполняется намного быстрее, чем выборочная дефолтная или зеро-инициализация каждого мембера в отдельности. Ты о чем вообще? Слово "сразу" - это обозначение отсутствия лишней логики. Вопрос производительности пока лично я не поднимал. Цитата Эм. Какую проблему создали и какой костылик подставили? Что в данном случае, на твой взгляд, является костылем? Избавление от рутинных и однообразных действий и выделение их в отдельную сущность? С каких пор это стало костылем? Цитата Ах, ну вот вам тогда костылик - заворачиваем любой указатель в фантик-класс под названием auto\unique_ptr - и вуа-ля, теперь он становится обычным subobject и по стандарту должен быть уничтожен вызовом собственного деструктора (если успел создаться до выброса исключения). Очень мило ![]() Так в чем костыльность-то? Т.е. писать освобождение руками - это не костыль, а автоматизировать это дело - костыль И да, ведь поля - это просто частный случай, а не основной. И не ради него вводилось управления ресурсами. Наши файловые потоки закрывают файлы сами, а не ждут отдельной команды... И так у нас обстоит дело с любыми ресурсами.Цитата В дельфи вообще нет такого понятия как subobject-ы, соотв-но нет и никакой жестко предопределенной иерархии автовызова конструкторов и деструкторов, и соотв-но нет понятий "сначала создали это, потом это" или "это уже создано, а это еще нет". Все создание объекта отдается на откуп программисту как "творцу\создателю" иерархии классов: хочешь\нужно вызвать конструктор родительского класса в дочернем конструкторе - вызывай, можно сразу перед своими действиями, а можно и после или между, или можно вообще ничего не вызывать и проинициализровать все по своему "с чистого листа" (если есть возможность, ес-но). В общем - каша. Ты даже не заметил, что "творец\создатель" у тебя в единственном числе... А реальные иерархии все-таки чаще создаются разными людьми. Цитата Зато имеется возможность введения полиморфизма в сам процесс конструирования объекта Так это не нужно. Вернее ради этого не надо жертвовать разделениями обязанностей - отделяем "полиморфную" логику(работу с уже созданными объектами) от собственно конструирования объектов. Цитата в том числе и за счет использования виртуальных методов. Ага, мы только предка конструируем, а уже дергаем методы недоделанного потомка. Супер просто Цитата PS: Разумеется все эти "зато" не могут служить аргументами в предвзятом споре в стиле "А из нашего окна площадь Красная видна, а из вашего окошка - только улица немножко!!!!" Это и так всем понятно. Но холивар же Добавлено Цитата leo @ Это костыль не сам по себе, а в сочетании с принципом не вызова деструктора "еще несозданного объекта" (по вашей терминологии), который просто вынуждает "порядочных" людей оборачивать указатели фантиками=костыликами Эм... Обетки не для этого создавались. А для управления ресурсами. Данное применение - просто следствие, потому под определение костыля не попадает. Цитата D_KEY еще и деструкторами не пользуется - их тоже выкинуть? Пользуюсь. Когда они нужны |
|
Сообщ.
#7195
,
|
|
|
|
Цитата D_KEY @ А реальные иерархии все-таки чаще создаются разными людьми. И что? Я тебе уже приводил аналогию (на которую ты мне ничего не ответил) что с таким же успехом можно придти к выводу что вызов виртуальных методов в базовом классе нужно запретить (не только из конструктора, а вообще). Ну, давай придем к такому выводу И на том и разойдемся зафиксировав разногласияЦитата D_KEY @ Ага, мы только предка конструируем, а уже дергаем методы недоделанного потомка. Супер просто Тебе уже двое написали что нет такого понятия "конструируем предка". Не читатель? |
|
Сообщ.
#7196
,
|
|
|
|
Цитата Qraizer @ Короче. Надёжнее. Легко обобщаемо э-э-э. Я правильно понял, что если нужно пять указателей хранить, то у меня пять уровней иерархии будет?? |
|
Сообщ.
#7197
,
|
|
|
|
Цитата --Ins-- @ Я тебе уже приводил аналогию (на которую ты мне ничего не ответил) что с таким же успехом можно придти к выводу что вызов виртуальных методов в базовом классе нужно запретить (не только из конструктора, а вообще). Ну, давай придем к такому выводу И на том и разойдемся зафиксировав разногласияЯ тебе в прошлой итерации отвечал на это Аналогия неверная, поскольку в случае виртуальных методов мне не нужно смотреть в реализацию базового класса, чтобы ожидать/сохранять свои инварианты. В случае конструирования мне приходиться или ожидать в любом перекрываемом методе некорректный("недоконструированный") объект или смотреть реализацию базового класса и исходить уже из конкретных аспектов этой реализации. Мы же обсуждали, забыл? Вы еще тогда путались в рекомендациях, когда вызывать конструктор базового класса, до инициализации полей или после |
|
Сообщ.
#7198
,
|
|
|
|
Цитата D_KEY @ Аналогия неверная, поскольку в случае виртуальных методов мне не нужно смотреть в реализацию базового класса, чтобы ожидать/сохранять свои инварианты Нужно. Виртуальный метод используется в коде предка и он мог не ожидать того, как ты захочешь поступить с ним в потомке |
|
Сообщ.
#7199
,
|
|
|
|
Цитата --Ins-- @ Нужно. Виртуальный метод используется в коде предка и он мог не ожидать того, как ты захочешь поступить с ним в потомке Пример, пожалуйста. |
|
Сообщ.
#7200
,
|
|
|
|
Цитата D_KEY @ Мы же обсуждали, забыл? Вы еще тогда путались в рекомендациях, когда вызывать конструктор базового класса, до инициализации полей или после И вправду не помню такого Все, что используешь в своих виртуальных методах и боишься что на этапе конструирования может не быть - инициализируй до. Добавлено Цитата D_KEY @ Пример, пожалуйста. Лень выдумывать. Есть код в базовом классе, который вызывает виртуальный метод. Ты его перекрываешь так, как проектировщик класса не рассчитывал. Разгребай. Ну, или лезь в код предка, но как я понял - тут программисту следует застрелиться |