
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.147] |
![]() |
|
Страницы: (117) « Первая ... 99 100 [101] 102 103 ... 116 117 ( Перейти к последнему сообщению ) |
Сообщ.
#1501
,
|
|
|
Вот только давай не будем повторять слухи, которые мы где-то и когда-то услышали. Я к примеру слышал, что паскаль был изобретён специально для обучения студентов и для реального использования не подходит... Но это же абсурд, все мы это понимаем ![]() Ах, чуть не забыл, Java и C# наверное изобретал тот же минималист Ричи, да? В С++ вообще нет таких понятий, как kernel-mode, user-mode, API, etc. В отличии от Delphi ![]() Цитата --Ins-- @ Я надеюсь вы сами прекрасно понимаете несерьезность таких аргументов ![]() Учитывая то, что даже я, далеко не новичок в С++, не знаю как сделать это без грязных хаков, это очень уж экзотическая возможность. Ага, ещё маленький ньюанс: не знаю, насколько я был "продвинутым" в Dephi, но если довериться квалифицированному мнению Смайка, который утверждал, что я dummy в Delphi, то меня несколько мущает тот факт, что мне, dummy в Delphi, было известно как пробиться к приватным членам класса и, даже по прошествии нескольких лет я могу это воспроизвести: ![]() ![]() class SomeExternalClass; procedure A.foo; var obj: SomeExternalClass; begin obj = SomeExternalClass.Create(); obj.somePrivateMethod(); end; Скажу лишь, что сделать forward-declaration класса намного проще(по крайней мере, по неопытности), чем сделать что-то, моему уму непостижимое, чтобы пробиться к приватным членам в С++(раз уж даже Флекс "только по бумажке"). Цитата --Ins-- @ Неужели нужно быть таким грамотным, чтобы прочесть в книжке, как получить доступ к приватному полю класса? Что за книжка такая? Я бы не отказался почитать ![]() Я не пойму, чего ты пытаешь этим доказать? В Delphi, ещё проще - не нужно вспоминать имя базового класса - просто пишешь base.foo(123) и, IIRC(давно на Delphi не программирую), IDE сама дописывает эту строку в конец виртуальных методов при их автоматической генерации(Ctrl+Alt+C или Ctrl+Shift+C - не помню уже). Добавлено Вспомнилась темка: насколько нужны хорошие знания программирования wind, почитайте ![]() |
Сообщ.
#1502
,
|
|
|
Цитата В С++ вообще нет таких понятий, как kernel-mode, user-mode, API, etc. В отличии от Delphi С этого момента - попрошу поподробнее ![]() Цитата что мне, dummy в Delphi, было известно как пробиться к приватным членам класса и, даже по прошествии нескольких лет я могу это воспроизвести: А мне до сих пор это не известно, а твой код ничего мне не говорит. И не только мне, он вообще ничего не показывает. Ты хоть сам понял, что написал? Если да, то лучше поясни на словах ![]() Цитата Я не пойму, чего ты пытаешь этим доказать? В Delphi, ещё проще - не нужно вспоминать имя базового класса - просто пишешь base.foo(123) Я не понимаю, что ТЫ пытаешься сказать? В Delphi вызов виртуального метода ВСЕГДА приводит к вызову СВОЕГО метода. Вызвать виртуальный метод, реализованный в предке, для объекта класса-потомка, у которого этот метод перекрыт, НЕВОЗМОЖНО без ковыряния в генофонде. Другими словами: ![]() ![]() T1 = class procedure Test; virtual; end; T2 = class(T1) procedure Test; override; end; ... var Obj: T2; begin Obj := T2.Create; Obj.Test; // Будет вызван метод класса T2 T1(Obj).Test; // Все равно будет вызван метод класса T2, т.к. Obj - является экземпляром именно этого класса (Obj as T1).Test; // Аналогично |
Сообщ.
#1503
,
|
|
|
Цитата --Ins-- @ Я не понимаю, что ТЫ пытаешься сказать? В Delphi вызов виртуального метода ВСЕГДА приводит к вызову СВОЕГО метода. Вызвать виртуальный метод, реализованный в предке, для объекта класса-потомка, у которого этот метод перекрыт, НЕВОЗМОЖНО без ковыряния в генофонде. Я бы не назвал это достоинством. Предположим наследник переопределяет метод базового класса. Ну типа базовый рисует круг, а потомок рисует треугольник вписаный в круг. Так вот если бы у потомка не было бы возможности вызвать метод базового класса, мне бы приходилось дублировать код в перекрываемом методе, что определенно есть не гуд. Добавлено Причем мой пример очень прост, а если логика сложнее? Получается, что код прийдется менять сразу в двух местах и помнить об этом? |
Сообщ.
#1504
,
|
|
|
juice, в реализации перекрытого метода потомка мы можем вызвать метод предка - для этого есть слово inherited. Ну, т.е. если мы перекрываем метод, который к реализации предка добавляет свой код, код предка дублировать нам не нужно. Но вот у клиентского кода возможности вызвать "неродной" виртуальный метод НЕТ! И это правильно. Так как иначе полиморфизм будет нарушен. В этом случае мы сможем в клиентском коде для экземпляра "треугольник в круге" вызвать метод, рисующий только "круг"
|
Сообщ.
#1505
,
|
|
|
Ну и в С# ты сможешь вызвать метод базового типа клиентом исключительно после приведения к базовому типу, в противном случае будет вызываться треугольник в круге. Насколько я помню точно так же ведет себя и плюсовый код.
|
Сообщ.
#1506
,
|
|
|
--Ins--, да чего ты привязался так к виртуальным методам предка. Чем тебе не нравится, кроме того, что в делфи такого нет? При любой попытке вызвать виртуальный метод предка происходит явное/неявное преобразование класса к предку. В каком пункте принципов ООП запрещено преобразование класса к предку? Если ты уже преобразовал класс к предку, откуда ему знать каким ты там методом перекрыл виртуальную функцию и в каком из дочерних классов? Опять же, если уж ты приобразовал класс к предку, что мешает вызывать именно его методы? Какой из принципов ООП это нарушает? А этот самый inherited -- это костыль. Его в паскале не было вплоть до появления делфи. Когда отсутствие вызова перегруженного предка стало просто мешать работать -- его прикрутили.
|
Сообщ.
#1507
,
|
|
|
Тут есть момент который продемонстрировал Impik. Возьмем тот же пример с кругом и треугольником вписываемым в него в наследнике. Предположим в классе наследнике есть еще метод который хочет отрисовать круг? Выходит в Delphi не получиться воспользоваться реализацией базового класса для рисования круга?
|
Сообщ.
#1508
,
|
|
|
Цитата Ну и в С# ты сможешь вызвать метод базового типа клиентом исключительно после приведения к базовому типу Ну и где тут полиморфизм? Простой пример: Дан класс TStream. В нем объявлены виртуальные методы Read и Write, которые ничего не делают. У класса есть бесконечное множество потомков - TFileStream, который позволяет писать/читать из файла, TMemoryStream, который позволяет читать/писать из памяти и т.д. Виртуальные методы в них перекрыты. Есть некий клиентский код - скажем, функция, у которой один из параметров имеет тип TStream. Эта функция, допустим, выполняет запись в поток, переданный ей в качестве параметра. Так как методы Write/Read объявлены как виртуальные, то, куда будет писать функция зависит от того, какому классу действительно принадлежит переданный ей параметр. При этом код ее от этого не зависит. Согласен ли ты с тем, что это нормальный полиморфизм? А теперь объясни, зачем клиентскому коду возможность вызывать реализацию методов класса TStream, а не перекрытых в потомках методов? Добавлено Цитата Если ты уже преобразовал класс к предку, откуда ему знать каким ты там методом перекрыл виртуальную функцию и в каком из дочерних классов? Ууу, как все запущено... Чем виртуальный метод от статического отличается знаешь? Про VMT слышал? А что подразумевается под поздним связыванием? Все он знает, поверь ты мне. ![]() |
Сообщ.
#1509
,
|
|
|
Цитата --Ins-- @ Ууу, как все запущено... Чем виртуальный метод от статического отличается знаешь? Про VMT слышал? Все он знает, поверь ты мне. ![]() А ну ка мне аж самому интересно... |
Сообщ.
#1510
,
|
|
|
Цитата Предположим в классе наследнике есть еще метод который хочет отрисовать круг? Выходит в Delphi не получиться воспользоваться реализацией базового класса для рисования круга? В реализации класса - все получится ![]() Добавлено Цитата А ну ка мне аж самому интересно... Недавно отвечал на вопрос, как реализованы виртуальные методы. Дублирую: Виртуальные методы имеется в виду? Если да, то примерно так: 1. Для каждого класса в статической памяти создается т.н. таблица виртуальных методов - VMT. В этой таблице хранятся адреса всех виртуальных методов, объявленных в данном классе и в классах-предках. 2. Одноименные виртуальные методы в классе-предке и потомке хранятся в таблице по одинаковым смещениям. Если метод в потомке не перекрывать, то в таблице потомка по данному смещению будет тот же адрес, что и в предке, если перекрыть - то в таблице по данному смещению будет адрес нового метода. 3. Каждый экземпляр любого класса хранит указатель на VMT своего класса. 4. Когда вызывается виртуальный метод (его адрес не известен на этапе компиляции, так как зависит от класса экземпляра, для которого метод вызывается, зато точно известно смещение метода в VMT - оно зависит только от вызываемого метода, а не от класса), генерируется примерно такой код: 4.1. Из экземпляра извлекается адрес VMT 4.2. Из VMT читается адрес, находящийся по известному на этапе компиляции смещению 4.3. Вызывается метод по адресу, полученному в 4.2. Т.е вместо call адрес как происходит для статических методов, генерируется примерно такой код: mov eax, адрес VMT mov eax, [eax + VMTOFFSET] call eax |
Сообщ.
#1511
,
|
|
|
Цитата --Ins-- @ А теперь объясни, зачем клиентскому коду возможность вызывать реализацию методов класса TStream, а не перекрытых в потомках методов? Первое, что всбрело в голову. Имеем базовый виртуальный метод Print, который выводит данные в консоль, потомки могут преопределять его как угодно, формируя вывод на принтер, порты или рисуя его в окнаx. Тогда когда мне захочется выводить данные исключительно в консоли я это смогу реализовать просто имея коллекцию объектов базового типа, проинициализированую всевозмжными экземплярами наследников и буду вызывать для всеx метод Print, приводя его к базовому типу. |
Сообщ.
#1512
,
|
|
|
juice
А я бы в этой ситуации поступил не так. Реализовал бы СТАТИЧЕСКИЙ метод PrintConsole в базовом классе, и виртуальный Print. В базовом классе виртуальный метод Print просто вызывал бы PrintConsole. В потомках - вел бы себя иначе. В результате я бы получил архитектуру, которая дает все то же самое, но не противоречит принципам ООП. Добавлено juice, кстати, в моем примере с TStream мы имеем объектную ссылку типа TStream? Неужели для вызова перекрытых методов нужно делать приведение к классам-потомкам? Уверен, что нет. |
![]() |
Сообщ.
#1513
,
|
|
Цитата Flex Ferrum @ Впрочем - с этим спором в соседнюю тему. Там примитивное решето Эрастофена на джаве со свистом слило плюсовому варианту... А ты говоришь - промышленность... ![]() Содержание последних сообщений той темы к вопросу не имеет вообще никакого отношения. И я ещё не разбирался с тем примером ![]() |
Сообщ.
#1514
,
|
|
|
Цитата --Ins-- @ Добавлено Вчера, 23:59 juice, кстати, в моем примере с TStream мы имеем объектную ссылку типа TStream? Неужели для вызова перекрытых методов нужно делать приведение к классам-потомкам? Уверен, что нет. Ты прав не нужно. Добавлено Правда иногда если я хочу более специфическое поведение, например позиционирование в потоке, которого нет к примеру в Stream, я могу рантайм проверить тип и если он позволяет это делать, то привести и вызвать нужный мне метод. |
Сообщ.
#1515
,
|
|
|
Цитата Правда иногда если я хочу более специфическое поведение, например позиционирование в потоке, которого нет к примеру в Stream, я могу рантайм проверить тип и если он позволяет это делать, то привести и вызвать нужный мне метод. Можно, но по хорошему, полиморфному клиентскому коду лучше не знать, с экземпляром какого класса он имеет дело. Позиционирование можно заложить и в TStream. |