
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.80.4.147] |
![]() |
|
![]() |
|
|
Есть наследник от TObjectList:
![]() ![]() type TMyList = class(TObjectList) private FSign: integer; function Compare(Item1, Item2: Pointer): Integer; public procedure Sort(aSign); overload; end; ... function TMyList.Compare(Item1, Item2: Pointer): Integer; begin if FSign > 0 then Result :=... else ... end procedure TMyList.Sort(aSign); overload; begin FSign := aSign; inherited Sort(Compare); //здесь на этапе компиляции возникает ошибка end; Проблема в том, что при передаче в Sort ф-ции Compare возникает ошибка как быть? Неужели я должен делать Compare глобальной? |
Сообщ.
#2
,
|
|
|
функция <> метод
|
Сообщ.
#3
,
|
|
|
Цитата Domino @ функция <> метод Ну так как передать, не ужели глобальную ф-цию заводить? |
Сообщ.
#4
,
|
|
|
Так у тебя Sort че принимает то? И че ты туда передаешь?
Она у тебя принимает функцию, а не метод. Ты же наследника делаешь, ну сделай, что бы он у тебя метод принимал, а предку функцию сделай, которая этот метод вызывать будет. |
Сообщ.
#5
,
|
|
|
Цитата Felan @ Type TCompareFunction = function(Item1, Item2: Pointer) of object; Передавать нужно простую функцию, а не метод. ![]() ![]() type TStringListSortCompare = function(List: TStringList; Index1, Index2: Integer): Integer; Добавлено Цитата DelphiLexx @ Неужели я должен делать Compare глобальной? Что значит глобальной? |
Сообщ.
#6
,
|
|
|
Цитата Felan @ Она у тебя принимает функцию, а не метод. Ты же наследника делаешь, ну сделай, что бы он у тебя метод принимал, а предку функцию сделай, которая этот метод вызывать будет. Что-то не совсем понял, можно на примере Добавлено Цитата Демо @ Передавать нужно простую функцию, а не метод. Понимаешь, я не хочу отделять ф-цию Compare от класса, мне не хотелось бы в моем классе, у которого есть метод Sort, чтобы он использовал вещи лежащие вне класса. Добавлено Цитата Демо @ Что значит глобальной? Как что, глобальная ф-ция - это ф-ция объявленная в секции interface и реализованная в секции implementation'a и не являющаяся методом какого-либо класса. |
Сообщ.
#7
,
|
|
|
Объяви ее только в implementation.
|
Сообщ.
#8
,
|
|
|
Цитата Romkin @ Объяви ее только в implementation. Не хотел бы я использовать в Sort, то что не принадлежит классу, не граммотно это. |
Сообщ.
#9
,
|
|
|
Цитата DelphiLexx @ Не хотел бы я использовать в Sort, то что не принадлежит классу, не граммотно это. Почему неграмотно? Как раз грамотно, когда функция сравнения - общая, и может использоваться разными классами. Строка ведь остается строкой, неважно, какому классу она принадлежит. И функцию сравнения строк делают общей. |
Сообщ.
#10
,
|
|
|
Цитата DelphiLexx @ Абсолютно согласен с этим утверждением. не граммотно это |
Сообщ.
#11
,
|
|
|
Цитата Domino @ Абсолютно согласен с этим утверждением Ну значит сортировку вручную реализовывать. Либо второй вариант - принять, что не класс является единицей реализации, а и модуль, в котором он описан. |
Сообщ.
#12
,
|
|
|
Цитата Domino @ Абсолютно согласен с этим утверждением. И зря. Какое отношение имеет оператор сравнения к контейнеру?! |
Сообщ.
#13
,
|
|
|
Цитата Domino @ Цитата (DelphiLexx @ Сегодня, 14:55) не граммотно это Абсолютно согласен с этим утверждением. Тоже согласен, но что поделать, если в данном случае в базовом классе именно функция? Не уверен, но может быть так попробовать: ![]() ![]() inherited Sort(TMethod(Compare).Code); //здесь на этапе компиляции возникает ошибка |
![]() |
Сообщ.
#14
,
|
|
Цитата Felan @ inherited Sort(TMethod(Compare).Code); //здесь на этапе компиляции возникает ошибка проверять лень, но по идее не должно быть ошибке на этапе компиляции.. Тока в run-time av'шка какая нить возникнет. |
Сообщ.
#15
,
|
|
|
Угу... возникает...
Ну, если сделать доступ потокобезопасный к Sort, то можно так извращьнуться ![]() ![]() ![]() unit Unit7; interface uses Contnrs; type TMyCompareFunction = function(aItem1: Pointer; aItem2: Pointer): integer of object; TMyObjectList = class(TObjectList) procedure Sort(aCompareFunction: TMyCompareFunction); reintroduce; end; implementation var vCF: TMyCompareFunction; function cmp(aItem1: Pointer; aItem2: Pointer): integer; begin Result := vCF(aItem1, aItem2 ); end; { TMyObjectList } procedure TMyObjectList.Sort(aCompareFunction: TMyCompareFunction); begin vCF := aCompareFunction; inherited Sort(cmp); end; end. Тогда получится что в наследнике как раз метод... |
Сообщ.
#16
,
|
|
|
Цитата DelphiLexx @ я не хочу отделять ф-цию Compare от класса, мне не хотелось бы в моем классе, у которого есть метод Sort, чтобы он использовал вещи лежащие вне класса. ...не граммотно это Глупости. Так можно дойти до того, что и функции из модулей StrUtils, Math и т.п. нельзя использовать, поскольку они не принадлежат классу ![]() Во-первых, можно просто функции сравнения дать осмысленное название (типа CompareTMyListItems) и разместить ее в implementation рядом с твоим Sort. Ничего страшного или неграмотного в этом нет - "я так тыщу раз делал" (С) Во-вторых, функция сравнения должна быть "глобальной" по сути, а не формально. Т.е. это может быть и вложенная функция внутри самой Sort при условии, что она не будет обращаться к локальным переменным функции Sort (включая Self и его поля). Пример: ![]() ![]() type TMylist = class(TList) public procedure Sort(Desc:boolean = false);overload; end; procedure tMyList.Sort(Desc:boolean = false); //вложенные функции сравнения function CompareAsc(Item1,Item2:pointer):integer; begin result:=integer(Item1)-integer(Item2); end; function CompareDesc(Item1,Item2:pointer):integer; begin result:=integer(Item2)-integer(Item1); end; begin if Desc then Sort(@CompareDesc) else Sort(@CompareAsc); end; procedure TForm1.Button1Click(Sender: TObject); var L:TMyList; S:string; i:integer; begin L:=TMyList.Create; try L.Add(pointer(2)); L.Add(pointer(1)); L.Add(pointer(3)); L.Sort; S:=''; for i:=0 to L.Count-1 do S:=S+IntTostr(integer(L[i])); L.Sort(true); S:=S+#13#10; for i:=0 to L.Count-1 do S:=S+IntTostr(integer(L[i])); ShowMessage(S); finally L.Free; end; end; |
![]() |
Сообщ.
#17
,
|
|
Felan кошмар, ужос!!!!!!!!!!!!!! У тебя же глобальная функция!!!!!!!!!!!!!
Добавлено Цитата leo @ то она не будет обращаться к локальным переменным функции Sort кста, немного по извращавшись с асмом можно сделать, чтоб она могла к локальным переменным обращаться. |
Сообщ.
#18
,
|
|
|
Цитата Felan @ Ну, если сделать доступ потокобезопасный к Sort, то можно так извращьнуться А вот ненужные глобальные переменные ИМХО это гораздо большее извращение, чем "глобальные" функции ![]() PS: Если метод нужен только для того, чтобы добраться до некоторого св-ва TMyList, то есть вариант использовать в элементах Item ссылку на список Parent:TMyList и в функции сравнения юзать if TMyItem(Item1).Parent.FSign then ... Цитата jack128 @ кста, немного по извращавшись с асмом можно сделать, чтоб она могла к локальным переменным обращаться В отношении Sort шибко сомневаюсь, т.к. она использует рекурсивные вызовы QuickSort, в которую передается не сам TList, а только массив указателей. Поэтому уже на втором уровне вложенности все локальные переменные Sort улетят далеко-далеко ![]() |
![]() |
Сообщ.
#19
,
|
|
Цитата leo @ В отношении Sort шибко сомневаюсь, т.к. она использует рекурсивные вызовы QuickSort, в которую передается не сам TList, а только массив указателей. Поэтому уже на втором уровне вложенности все локальные переменные Sort улетят далеко-далеко ![]() Ну вот такой код: ![]() ![]() procedure TForm3.Test; var LocalCounter: Integer; function LocalCompare(Item1, Item2: Integer): Integer; begin Inc(LocalCounter); Inc(GlobalCounter); Result := Item1 - Item2; end; var I: Integer; L: TList; begin Randomize; GlobalCounter := 0; LocalCounter := 0; L := TList.Create; try for I := 0 to 100 - 1 do L.Add(Pointer(Random(100))); L.Sort(LocalProcCaller(@LocalCompare).Proc); // вся магия в LocalProcCaller. там динамически генерится код, подправляющий стек. Memo1.Lines.Clear; for I := 0 to L.Count - 1 do Memo1.Lines.Add(IntToStr(Integer(L[I]))); Memo1.Lines.Add(Format('LocalCount = %d, GlobalCounter = %d', [LocalCounter, GlobalCounter])) // LocalCount всегда равен GlobalCounter. AV нигде нет. Сортируется тоже нормально.. finally L.Free; end; end; Скажу чесно сам я в асме не силен, идею почерпнул из модуля AsyncCalls.pas. Если интересно могу выложить исходники LocalProcCaller, может багу кто найдет. |
Сообщ.
#20
,
|
|
|
jack128 ААААА!!!!!!!!!! Убей меня об стенку скореее!!!!!!!!!!
![]() Так-то да, но наследники то уже будут метод использовать, а не глобальную функцию, однажды и навсегда скрутую в нутри класса. leo Так ты самую вкусность скрыл. Теперь извне классу не подставишь "компоратор". Если раньше можно было для любого объекта сделать функцию сравнения, передать ее в список и он бы сортировал что угодно, то теперь только то, что ты пропишешь и ничего более. |
Сообщ.
#21
,
|
|
|
Может хватит, а? А то впечатление, что здесь одни извращенцы собрались.
Функция сравнения, как я уже сказал, сделана именно функцией совсем не случайно. Можно было ее сделать методом класса, но не сделали потому, что контейнер TObjectList может хранить и "чужие" объекты, у которых метода сравнения нет и сделать нельзя (о хелперах не говорю). |
Сообщ.
#22
,
|
|
|
Цитата jack128 @ вся магия в LocalProcCaller. там динамически генерится код, подправляющий стек Ну ежели динамически, то конечно ![]() Цитата Felan @ Так ты самую вкусность скрыл. Теперь извне классу не подставишь "компоратор". Если раньше можно было для любого объекта сделать функцию сравнения, передать ее в список и он бы сортировал что угодно, то теперь только то, что ты пропишешь и ничего более "Скрыл" только для примера, по заявкам трудящихся антиглобалистов ![]() К тому же за счет overloaded оригинальный Sort остается - подставляй, что хочешь Добавлено Цитата Romkin @ Можно было ее сделать методом класса, но не сделали потому, что контейнер TObjectList может хранить и "чужие" объекты, у которых метода сравнения нет и сделать нельзя (о хелперах не говорю) Странное объяснение ![]() |
Сообщ.
#23
,
|
|
|
Цитата leo @ Значит сравнивать "несравнимые" элементы разрешили (через функцию), но иметь при этом доступ к некому объекту (владельцу функции сравнения) - ни-изя ?! А зачем и с какой целью? Тебе даны две сущности - их и сравни. Заодно при смене контейнера функцию менять не надо, она уже есть. Каким образом результат сравнения двух сущностей может зависеть от экземпляра контейнера, в котором они содержатся? |
![]() |
Сообщ.
#24
,
|
|
Цитата Romkin @ Каким образом результат сравнения двух сущностей может зависеть от экземпляра контейнера, в котором они содержатся? э-э-э. TStrings.CompareStrings например. |
Сообщ.
#25
,
|
|
|
Цитата jack128 @ э-э-э. TStrings.CompareStrings например. И шо? Там строки, все сразу понятно. И от контейнера тоже не зависит. Добавь к этому, что TStrings - абстрактный класс, ты обязан написать потомка. А вот TStringList.CustomSort опять использует функцию, экземпляр в которую передается только потому, что неизвестно, какой элемент ты хочешь сравнивать, саму строку, или объект, и даются индексы. И для реализации сравнения не нужно писать потомка. Это удобно и разумно. И криминала здесь никакого нет. Вообще. |
![]() |
Сообщ.
#26
,
|
|
Цитата Romkin @ И от контейнера тоже не зависит. ну как же - смотрим его наследника, TStringLIst и что мы видим ?? Функция сравнения использует данные класса. |
Сообщ.
#27
,
|
|
|
Цитата jack128 @ ну как же - смотрим его наследника, TStringLIst и что мы видим ?? Функция сравнения использует данные класса. Хм. Действительно. Выбор нужной функции для сравнения. Но абсолютно не мешает подать соответствующую функцию compare в Sort. Leo чуть выше практически это и сделал. Так что криминала все равно нет. А вот если бы TList использовал свою виртуальную функцию, тебе пришлось бы на каждый чих писать его наследника. |
![]() |
Сообщ.
#28
,
|
|
Цитата Romkin @ А вот если бы TList использовал свою виртуальную функцию, а никто этого и не предлагает. ПРедлагают чтоб List.Sort принемал не функцию, а метод. |
Сообщ.
#29
,
|
|
|
Цитата Romkin @ Каким образом результат сравнения двух сущностей может зависеть от экземпляра контейнера, в котором они содержатся? Во-первых, не обязательно самого контейнера, т.к. метод сравнения может принадлежать и не контейнеру, а любому другому объекту (например владельцу этого контейнера). Во-вторых, сущности могут сравниваться по разному и правило сравнения ес-но задают не сами сущности, а тот кто их сравнивает. Например, можно написать две разные функции сравнения для сортировки по возрастанию и по убыванию, а можно несколько поступиться быстродействием и впихнуть все в одну функцию, инвертирующую рез-т сравнения в завис-ти от некоторого св-ва самого контейнера или другого объекта. В общем, задачи разные бывают и впадать из одной крайности (ха-ачу метод) в другую (ни-изя метод) - имхо не стоит ![]() |
Сообщ.
#30
,
|
|
|
Цитата leo @ В общем, задачи разные бывают и впадать из одной крайности (ха-ачу метод) в другую (ни-изя метод) - имхо не стоит А это не крайность, это вопрос удобства. В данном случае метод просто унаследован от TList, а там чаше всего требование именно метода ограничивает, мало ли на что я там ссылки хранить буду. А мотивов переделывать Sort на получающий метод я не вижу. |
Сообщ.
#31
,
|
|
|
Цитата Romkin @ А мотивов переделывать Sort на получающий метод я не вижу Во-первых, не переделывать, a overload-ить для удобства использования. Во-вторых, мотив прозвучал "во первЫх строках" сей темы - народ с подачи самого Борланда привык к классам и методам и соотв-но требует "хлеба и зрелищ" ![]() ![]() |
Сообщ.
#32
,
|
|
|
Вообщем, моя точка такова, сделано не совсем у Borland'а не совсем правильно, нужно было реализовать два варианта когда compare должна быть методом и когда должна быть общей (глобальной) и не было бы проблем.
|
Сообщ.
#33
,
|
|
|
Цитата Romkin @ В данном случае метод просто унаследован от TList, а там чаше всего требование именно метода ограничивает, мало ли на что я там ссылки хранить буду Да ниче не ограничивает. Как тут уже сказали, этот метод не обязательно должен принадлежать контейнеру. Он может принадлежать элементу. А элементы могут быть разными, но реализовывать один интерфейс для сравнения, если по смыслу задачи это надо. Собственно пофиг че пользовать, метод или функцию. Просто в свете современных веяний, и того, что в новых языках и т.п. и т.д. вообще не может быть никаких функций не принадлежащих никакому классу. И, лично я, считаю, что это правильно. Раз идеология с процедурной сменилась на объектную, нефиг за собой артефакты таскать. И с точки зрения цельности абстракции никому не принадлежащая функция как-то странно выглядит... Вобщем, ИМХО, это пережиток. Надо все-таки в сторону методов смотреть. |
Сообщ.
#34
,
|
|
|
Цитата Felan @ Как тут уже сказали, этот метод не обязательно должен принадлежать контейнеру. Он может принадлежать элементу. А элементы могут быть разными, но реализовывать один интерфейс для сравнения, если по смыслу задачи это надо. Цитата Romkin @ Можно было ее сделать методом класса, но не сделали потому, что контейнер TObjectList может хранить и "чужие" объекты, у которых метода сравнения нет и сделать нельзя (о хелперах не говорю). Зачем писать лишнее? Все сделано грамотно. |
Сообщ.
#35
,
|
|
|
Цитата Felan @ Раз идеология с процедурной сменилась на объектную, нефиг за собой артефакты таскать. И с точки зрения цельности абстракции никому не принадлежащая функция как-то странно выглядит... Полностью с тобой согласен |
Сообщ.
#36
,
|
|
|
Ладно, все равно уже наоффтопили...
Цитата Felan @ Собственно пофиг че пользовать, метод или функцию. Просто в свете современных веяний, и того, что в новых языках и т.п. и т.д. вообще не может быть никаких функций не принадлежащих никакому классу. И, лично я, считаю, что это правильно. А я в этом не уверен. Возьмем Java или C#. На 100% объектный? Многим покажется что да. Но как тогда объяснить такое загадочное явление, как статические методы, которые ни к объектам ни к классам никакого отношения вообще не имеют, если разобраться? А ведь они введены как раз из-за того, что в 100% объектном языке нет и не может быть бесхозных процедур/функций. Просто произошла подмена понятий и класс взял на себя функцию пространства имен, которую в Delphi традиционно выполняет юнит. Добавлено Кстати, а как быть, когда мне нужна сортировка по разным критериям одновременно? У какого класса делать методы сравнения? У контейнера или элементов? Если первое, то в классе контейнере в общем случае невозможно учесть все типы элементов, которые могут в нем содержаться. Если второе, то в классе элементов невозможно учесть все критерии, по которым я захочу их сравнивать. Если делать гибко, то ничего лучше внешней функции не придумаешь. Ну, разве что классы-сортировщики с единственным статическим методом Compare, но эта "красота" за уши притянута, а почему - я писал выше. |
Сообщ.
#37
,
|
|
|
Ins, хорошее замечание
![]() Действительно, если объявить компаратор как метод, и если у меня нет класса (например, я использую класс списка в простой процедуре и мне надо его отсортировать), то я вынужден писать такое: ![]() ![]() type TDummy = class class function Compare(Item1, Item2: Pointer): Integer; end; class function TDummy.Compare(Item1, Item2: Pointer): Integer; begin // ... end; Вместо: ![]() ![]() function Compare( { List: TSomeList; } Item1, Item2: Pointer): Integer; begin // ... end; Оба варианта (метод/функция) являются удобными в каких-то отдельных случаях. И нет универсально удобного способа. Если отбросить в сторону стилевые предпочения (и охота вам проблемы на пустом месте создавать), то самый важный момент связан с тем, можете ли вы пердать внешние данные в компаратор или нет. Т.е. с передачей user-data. Если компаратор является методом, то все дополнительные данные могут передаваться через объект - в конце концов метод это же код + данные. Если компаратор является функцией, то нет уже умалчиваемого способа задания внешних данных. Обычно такую передачу делают через обобщённый user-param типа Pointer или Cardinal. Через него можно передать любые данные, какие вам надо. Например, как это сделано в TStringList: ![]() ![]() TStringListSortCompare = function(List: TStringList; Index1, Index2: Integer): Integer; Окей, компаратор TObjectList не имеет user-param-а. Почему? Хороший вопрос ![]() ![]() ![]() type TMyItemList = class; TMyItem = class(...) private FList: TMyItemList; end; TMyItemList = class(TObjectList) ... end; function SortMyListByName(Item1, Item2: Pointer): Integer; var List: TMyItemList; begin List := TMyItem(Item1).FList; // ... end; |
Сообщ.
#38
,
|
|
|
Цитата --Ins-- @ А я в этом не уверен. Возьмем Java или C#. На 100% объектный? Многим покажется что да. Но как тогда объяснить такое загадочное явление, как статические методы, которые ни к объектам ни к классам никакого отношения вообще не имеют, если разобраться? Если разобраться, то методы относятся к экземпляру класса, а статические методы к классу. Класс это абстракция. Основное понятие ООП. Плюс этому то, что говорил CodeMonkey про параметризацию компоратора. А в отдельный класс эти методы выносить или для каждого свой, или еще как-то это вопрос дизайна, который каждый для себя решает сам. Нет понятия правильного/неправильного дизайна. Есть лучший/худший, и многие "законодатели мод" считают что объектный дизайн лучше, чем процедурный. ![]() |
Сообщ.
#39
,
|
|
|
Цитата Felan @ а статические методы к классу. А ты не путаешь class methods Delphi со static-методами Джавы и Шарпа? Эт не одно и то же ![]() |
Сообщ.
#40
,
|
|
|
Нет. Не путаю.
Это просто абстракция, но это средства упорядочивания логической структуры. Покажи мне пример, как вызвать статический метод джавы или шарпа без имени класса. Без извращений с отражениями. Если сможешь, посыплюсь пеплом и признаю твою правоту, иначе ты не прав. |
Сообщ.
#41
,
|
|
|
Felan, ты не понял. Смотри, классовый метод в Delphi действительно работает с классом. А статик-метод в Java - нет. Класс для него является лишь простанством имен, подобно тому, как для обычной процедуры пространством имен в Delphi является юнит. Так в чем же принципиальная разница между статик-методом и функцией/процедурой? В том, что для вызова статик-метода пространство имен указывать обязательно, а для обычной процедуры - нет?
![]() Добавлено Вообще, почему в TList параметр функция, а не метод - понятно. TList - это контейнер произвольных данных, не обязательно объектов, так зачем же вешать объектную оболочку над необъектной сущностью. Да и исторически так сложилось. Почему не сделали overload-метод у TObjectList, скажем? Да фиг его знает, не посчитали необходимым. Но разве тяжело ввести его самому? Если пишешь наследника - просто объявляешь свой метод, если используешь сам TObjectList - хелпер в руки. ![]() |
Сообщ.
#42
,
|
|
|
Цитата --Ins-- @ Так это чаще всего преимущество. Не нужно ради одной подпрограммы городить целый класс и каждый раз его указывать. + 1. Тот же Delphi например. Зачем надо было RaiseOuterException делать простым статическим методом класса? Теперь вместо компактного (возможного) варианта: ![]() ![]() try ... except on E: Exception do begin ... raiseOuter(Exception.Create(...)); end; end; Нужно писать: ![]() ![]() try ... except on E: Exception do begin ... Exception.RaiseOuterException(Exception.Create(...)); end; end; А чего стоит ![]() ![]() TCharacter = class sealed состоящий только из статических классовых методов! Причём, чтобы не корячится каждый раз с TCharacter.ToLower, в этом же модуле введены процедуры-переходники. Которые ровным счётом ничего не делают, кроме как вызывают методы TCharacter. Ужас. Не иначе как для совместимости с ???... не знаю, .NET, может быть. Скоро Integer станет sealed-классом со статическими операторами плюс и минус ![]() Цитата --Ins-- @ хелпер в руки Да-да-да, отличная вещь! ![]() |
Сообщ.
#43
,
|
|
|
Цитата --Ins-- @ Felan, ты не понял. Смотри, классовый метод в Delphi действительно работает с классом. А статик-метод в Java - нет. Класс для него является лишь простанством имен, подобно тому, как для обычной процедуры пространством имен в Delphi является юнит. Так в чем же принципиальная разница между статик-методом и функцией/процедурой? В том, что для вызова статик-метода пространство имен указывать обязательно, а для обычной процедуры - нет? ![]() Это ты не понял. ![]() Да, "...класс для него лишь пространство имен..." и это позволяет более правильно и строго следить за структурой проекта, областями видимости и т.п. Принципиальной разницы нет. Так же как ее нет в итоге между программой написанной на разных языках (не будем разводить флейм про скорость и подобное). Еще раз говорю, это всего лишь средство организации абстракций из которых состоит проект. И сделано это для того, что бы не использовать одновременно две парадигмы, процедурную и объектную. С точки зрения процедурной парадигмы - ты прав. С точки зрения объектно-ориентированной - я прав. Остается выяснить какая парадигма правильная ![]() ![]() ![]() Но мы же не будем этим бредом заниматься? Лично мне нравится объектная. Она мне удобнее. ![]() ЗЫЖ Предлагаю мировую. А то это никогда не кончится ![]() |
![]() |
Сообщ.
#44
,
|
|
Цитата Felan @ Принципиальной разницы нет в Java есть виртуальные классовые методы?? |