Delphi vs C++ vs C#
, ну и Java немножко, где-то ближе к старшему байту номеров страниц
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.156] |
|
|
Правила раздела:
| Страницы: (495) « Первая ... 139 140 [141] 142 143 ... 494 495 ( Перейти к последнему сообщению ) |
Delphi vs C++ vs C#
, ну и Java немножко, где-то ближе к старшему байту номеров страниц
|
Сообщ.
#2101
,
|
|
|
|
korvin, и вывод будет
![]() ![]() A.f B.f B.f B.g ? Странно, не ожидал |
|
Сообщ.
#2102
,
|
|
|
|
Цитата D_KEY @ 1. У той, "тип" которой ты описываешь своим интерфейсом 2. Ты не понял о чем я Дело не в том, чтобы задать сущности явное имя, дело в создании лишней сущности тогда, когда она не нужна.так еще раз: о какой сущности идет речь? интерфейсом я определяю, что все объекты, для которых реализованы методы foo и bar являются объектами типа I |
|
Сообщ.
#2103
,
|
|
|
|
|
Сообщ.
#2104
,
|
|
|
|
Цитата MyNameIsIgor @ korvin, и вывод будет ![]() ![]() A.f B.f B.f B.g ? Странно, не ожидал ![]() я ж говорю, проверить не могу, но должен быть такой. а что тут странного? задача сводится всего лишь к перекрытию метода предка и приведению объекта-потомка к типу объекта предка, чтобы в первый раз в f1 вызвался метод предка. во всех остальных случаях будет вызван метод потомка. в Racket такое сделать неудасться -- там нет приведения типов в принципе. в CL тоже не удастся, несмотря на наличие множественного наследования Добавлено Цитата MyNameIsIgor @ так он это скажет перед раскрытием шаблона или уже после, на раскрытом коде? |
|
Сообщ.
#2105
,
|
|
|
|
Цитата korvin @ я ж говорю, проверить не могу, но должен быть такой. а что тут странного? задача сводится всего лишь к перекрытию метода предка и приведению объекта-потомка к типу объекта предка, чтобы в первый раз в f1 вызвался метод предка. во всех остальных случаях будет вызван метод потомка. В том то и дело, что, например, в C# реализация интерфейса делает метод виртуальным. И впоследствии его переопределение в потомке приводит к тому, что даже при касте потомка в предка вызывается метод потомка, как и должно быть с виртуальными методами. Добавлено Цитата korvin @ так он это скажет перед раскрытием шаблона или уже после, на раскрытом коде? Ээээ... А что такое "раскрытие шаблона"? |
|
Сообщ.
#2106
,
|
|
|
|
Цитата D_KEY @ Кто кого заменяет - это еще вопрос. Интерфейс явно вводит новый "тип" и требует, чтобы аргумент ему соответствовал. Не очень-то соответствует "утиной типизации" которая нам тут, фактически, нужна. Шаблон же говрит, что тип может быть любым, но требуется, что вот такие-то операции можно осуществить с объектами этого типа. дык некий набор операций -- это фактически и есть абстрактный тип данных =) другое дело, что возможно было бы удобней, чтобы интерфейсы не занимали пространство имен типов данных, однако множество пространств имен тоже не в каждом языке хорошо, вот в CL -- хорошо, а для топиковых языков, имхо, не очень |
|
Сообщ.
#2107
,
|
|
|
|
Цитата korvin @ я ж говорю, проверить не могу, но должен быть такой. а что тут странного? задача сводится всего лишь к перекрытию метода предка и приведению объекта-потомка к типу объекта предка, чтобы в первый раз в f1 вызвался метод предка. во всех остальных случаях будет вызван метод потомка. Кстати, а как тогда будет выглядеть код, если я захочу в B заменить реализую f из A, чтобы любой код, ожидающий A, использовал новую реализацию? |
|
Сообщ.
#2108
,
|
|
|
|
Цитата korvin @ Цитата D_KEY @ Так ты ответишь, чем тебе не нравится: ![]() ![]() template<typename T> void f(T x) { x.foo(); x.bar(); } ? тем, что шаблон никак не может проверить, реализует ли переданный ему тип нужный интерфейс (методы foo и bar) Тебе будет сказано, что в таком-то шаблоне, параметаризированном таким-то типом, произошла ошибка, которая заключалось в том, что у объектов данного типа нет таких методов. Простыни ошибок получаются тогда, когда слишком много шаблонов(компилятор описывает тебе полностью весь контекст возникшей ошибки). Концепты решают эту проблему, но их не будет в новом стандарте |
|
Сообщ.
#2109
,
|
|
|
|
Цитата MyNameIsIgor @ В том то и дело, что, например, в C# реализация интерфейса делает метод виртуальным. И впоследствии его переопределение в потомке приводит к тому, что даже при касте потомка в предка вызывается метод потомка, как и должно быть с виртуальными методами. да, я в курсе, в делфи методы по-умолчанию не виртуальны, для этого есть ключевое слово virtual. в общем завтра попробую, или может сегодня кто из местных делфистов расставит точки над i Цитата MyNameIsIgor @ Ээээ... А что такое "раскрытие шаблона"? инстанциирование исходник шаблона: ![]() ![]() template<typename T> void f(T x) { x.foo(); x.bar(); } инстанциирование: ![]() ![]() f(<Type> var); раскрывается в ![]() ![]() // somewhere... void f_123456(Type x) { x.foo(); x.bar(); } ... f_123456(var) т.е. при инстанциировании "где-то там" объявляется функция с уникальным именем и это имя уже подставляется в инстанциированном вызове. ну это грубо и м.б. уже не так все, раньше вроде примерно так шаблоны работали |
|
Сообщ.
#2110
,
|
|
|
|
Цитата korvin @ инстанциирование Ну, да, во время инстанцирования будет ошибка. А как же без инстанцирования то сказать, что мы ошиблись? Только, разумеется, никакого описанного макросоподобия не будет. Добавлено Цитата korvin @ да, я в курсе, в делфи методы по-умолчанию не виртуальны, для этого есть ключевое слово virtual. В шарпе так же, что не мешает описанному поведению. |
|
Сообщ.
#2111
,
|
|
|
|
Цитата MyNameIsIgor @ Кстати, а как тогда будет выглядеть код, если я захочу в B заменить реализую f из A, чтобы любой код, ожидающий A, использовал новую реализацию? эм... не совсем понял. если ты в код, ожидающий А, передашь объект класса А, то будет вызван метод класса А. нельзя в потомке переопределить метод предка так, чтобы в инстансах предка он тоже поменялся. или ты имеешь в виду, что, при передаче В всегда вызывался метод В, даже если в А этот метод определен не как виртуальный? тогда не знаю, надо поэкспериментировать, я с этими virtual/override/overload давно не заморачивался, так что не помню что там да как |
|
Сообщ.
#2112
,
|
|
|
|
Цитата MyNameIsIgor @ Дык не то ведь! Функции должны принимать интерфейсы, об их конкретных реализациях они ничего не знают. Ну, может, если так переделаю, станет понятнее ![]() ![]() #include <iostream> class IA { public: virtual void f() = 0; }; class IB : public IA { public: virtual void g() = 0; }; class A : public IA { public: virtual void f() { std::cout << "A::f()" << std::endl; } }; class Base : public IB { void f() { IB_f(); } public: virtual void IB_f() = 0; }; class B : public A, public Base { public: virtual void g() { std::cout << "B::g()" << std::endl; } virtual void IB_f() { std::cout << "B::f()" << std::endl; } }; void f1(IA& iface) { iface.f(); } void f2(IB& iface) { iface.f(); iface.g(); } int main() { B b; f1(static_cast<A&>(b)); f1(static_cast<IB&>(b)); f2(b); return 0; } ![]() ![]() A::f() B::f() B::f() B::g() ![]() ![]() type IA = interface procedure f; end; IB = interface (IA) procedure g; end; A = class(TInterfacedObject, IA) procedure f; virtual; end; B = class (A, IB) procedure f; override; procedure g; end; procedure A.f; begin Writeln( 'A.f' ); end; procedure B.f; begin Writeln( 'B.f' ); end; procedure B.g; begin Writeln( 'B.g' ); end; procedure f1 (const x : IA); begin x.f; end; procedure f2 (const x : IB); begin x.f; x.g; end; var x : B; begin x := B.Create; f1( x as A); f1( x ); f2( x ); end; ![]() ![]() B.f B.f B.f B.g Цитата MyNameIsIgor @ В том то и дело, что, например, в C# реализация интерфейса делает метод виртуальным. И впоследствии его переопределение в потомке приводит к тому, что даже при касте потомка в предка вызывается метод потомка, как и должно быть с виртуальными методами. Замечательно, подведём итоги: Delphi + C# + C++ - Добавлено Ну вы ещё в полиморфизме используйте static_cast и называйте нас ненормальными Добавлено Цитата MyNameIsIgor @ Кстати, а как тогда будет выглядеть код, если я захочу в B заменить реализую f из A, чтобы любой код, ожидающий A, использовал новую реализацию? Если правильно понял, то также как и у меня. |
|
Сообщ.
#2113
,
|
|
|
|
Цитата korvin @ Ну, первое не проблема. А вот второе - категорическое нет. Это методы разных интерфейсов. С какого перепуга они окажутся одним и тем же? Имена совпали? Бывает. Кто виноват? Да никто. Интерфейс IHistory может иметь право иметь метод LifeTime()? А интерфейс ITCP? А как быть чатилке, которой понадобился компонент, реализующий оба? Знаешь, я предпочту ошибку компиляции, чем молчаливое слияние в один метод.1) совпадают только имена, количество и типы параметров разные -- просто два перегруженных метода. ты ж с Java знаком? 2) совпадают полностью сигнатуры -- никакой колизии, просто один и тот же метод, в чем проблема? пересечение множеств всего-навсего Нет разницы, множественно наследуются интерфейсы ли или их реализации. Случайные коллизии имён от этого не пропадут сами собой. Вон, Shaggy показал приятный сахарок, сделать который "имеющимися средствами", как показал Повстанець, несложно через промежуточную реализацию, переименовав конфликтующие методы. Зато Shaggy лишён возможности наследовать готовые реализации ITCP и IHistory, написанные с год назад для разных проектов. Вот объединить бы оба плюса в одни Плюсы... Ну, забыть поставить можно и очередной override. Цитата DesweR @ DesweR, как обычно, ничего не понял, но вставил. Речь шла о том, что при передаче в функцию, ожидающую интерфейс A, и получая в ней поведение интерфейса B - это только в Delphi и C# считается за +. Интересно посмотреть на счастливого программера, меняющего время жизни TCP-пакета и получающего вместо этого смену длины истории диалога.Delphi + C# + C++ - Цитата DesweR @ Я-таки вижу в Дельфийном коде AS? Или мне мерещится? ...Ах да, он же Ну вы ещё в полиморфизме используйте static_cast и называйте нас ненормальными Если кто не понял. А то как слепые, такое ощущение. static_cast<> там играет роль селектора реализаций интерфейса A. Если уж горе-дизайнер надизайнил такую иерархию, что бедному Повстанцу теперь за ним разгребать, то выбор требуемой одной из двух копий реализации A выполняется одним чихом. "А у вас?" © |
|
Сообщ.
#2114
,
|
|
|
|
Цитата Qraizer @ Цитата DesweR @ DesweR, как обычно, ничего не понял, но вставил.Delphi + C# + C++ - Да я вообще в осадок выпал... Вот что сказать человеку, утверждающему, что небо красное? ![]() Цитата DesweR @ Ну вы ещё в полиморфизме используйте static_cast и называйте нас ненормальными Привидение потомка к предку и предка к потомку - две большие разницы. Цитата DesweR @ Если правильно понял, то также как и у меня. Слава Богу, хоть что-то поняли... Только сначала я просил как раз иное поведение. И уже после того, как korvin привёл код, который по моим представления им не обладал (что вы и подтвердили), я спросил, как же тогда будет выглядеть код с переопределение метода класса A в B. |
|
Сообщ.
#2115
,
|
|
|
|
Цитата Qraizer @ Ну, первое не проблема. А вот второе - категорическое нет. Это методы разных интерфейсов. С какого перепуга они окажутся одним и тем же? Имена совпали? Бывает. Кто виноват? Да никто. Интерфейс IHistory может иметь право иметь метод LifeTime()? А интерфейс ITCP? А как быть чатилке, которой понадобился компонент, реализующий оба? Знаешь, я предпочту ошибку компиляции, чем молчаливое слияние в один метод. это ООПшно, объект не может и не должен реагировать на одно и то же сообщение по-разному. Добавлено Цитата Qraizer @ Ну, забыть поставить можно и очередной override. дык конечно, задолбали все эти лишние сущности Добавлено Цитата Qraizer @ DesweR, как обычно, ничего не понял, но вставил. Речь шла о том, что при передаче в функцию, ожидающую интерфейс A, и получая в ней поведение интерфейса B - это только в Delphi и C# считается за +. это вообще-то везде так, кроме C++. |