Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Delphi: Общие вопросы > Заблудился в ООП


Автор: k.sovailo 29.05.17, 18:42
Доброго времени суток. Начал разбираться с ООП и заблудился, суть проблемы такая: есть массив
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    DrawList: array of TF;

Это список, который составляется одной процедурой и рисуется другой. Определение этого такое:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    type TF = class
      StrField: array of TStrField;
      IntField: array of TIntField;
      constructor Create(Name: string; ID: word); overload; virtual;
    end;
     
     
    type TT = class(TF)
      Font: TFont;
      Indent: word;
      FullText: TStringList;
      constructor Create(Name: string; ID: word); override;
      procedure Draw;
    end;
     
    type TypO = class(TF)
      Sprite: array of TSprite;
      constructor Create(Name: string; ID: word); override;
      procedure Draw;
    end;

Все данные типа размера, координат, видимости и прозрачности записано в TIntField. Прошу поверить на слово, что так было надо. При составлении списка проблем не возникло. А в рисовании проблема. Для производительности делается проверка всяких мёртвых, пустых и отключенных объектов. И тут возникло Invalid class typecast в этих строчках:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        if DrawList[i] is TypO then begin
          if (DrawList[i].IntField[1].Value>0)and(DrawList[i].IntField[7].Value>0) then
            (DrawList[i] as TypO).Draw;
        end else begin
          if (drawList[i].IntField[1].Value>0)and((DrawList[i] as TT).FullText.Count<>0) then
            (DrawList[i] as TT).Draw;
        end;

Тут as вообще нигде не работает. Подскажите, пожалуйста, почему?

Автор: Pavia 29.05.17, 18:51
Либо вы пробуете сконвертировать неправильный тип либо пытаетесь сконвертировать несуществующий объект.
Добавьте проверку if DrawList[i] <> nil then

Автор: ViktorXP 29.05.17, 20:38
а какое отношение выше приведенный код имеет к ООП?
1) наследование есть
2) полиморфизма нет
3) а самое главное нет абстрактных классов. он как бы есть. но как бы и нет


для повишения читабельности кода и его упрощении:
а) метод "Draw" сделай виртуальный+абстрактный в TF классе.
б) решение рисовать или не рисовать нужно делать или в самом методе "Draw" (что не красиво) или вынести как отдельный метод. (например isDraw())

тогда конечный код процедуры будет выглядеть
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        for item in DrawList do
           if item.isDraw() then
               item.Draw();


Добавлено
ну и массив смотреться не особо красиво "DrawList: array of TF;" не проще ли использовать дженерики TList<TF>. они и по практичнее будут, и дефрагментацию массива не придется самому делать

Автор: leo 30.05.17, 04:50
Цитата k.sovailo @
Тут as вообще нигде не работает. Подскажите, пожалуйста, почему?

Потому, что DrawList[i] содержит ссылку, не равную nil и не приводимую ни к TypO, ни к TT. Либо это ссылка на объект TF, либо на удаленный объект, либо вообще какой-то мусор (например, за счет выхода индекса за пределы массива).

Добавлено
Цитата Pavia @
Добавьте проверку if DrawList[i] <> nil then

Добавить, конечно, не помешает. Но в данном случае дело не в этом, т.к. ошибка возникает именно на операторе as, для которого nil является допустимым значением (nil as Х = nil).

Автор: k.sovailo 30.05.17, 14:59
Спасибо всем, ошибку нашёл и она была очень дурацкой.
leoправ, по нужной ссылке лежал мусор. При составлении списка было не DrawList[i]:=T[j], а DrawList[i]:=@T[j]; Перед этим менял тип DrawList'a и собака осталась. Pavia, спасибо, проверку добавил, хотя она не спасла. ViktorXP, извините, вашим советом не воспользуюсь, так как проблема решилась.

Можно ли тогда вопрос в догонку: а насколько вообще быстро работают переходы по ссылкам, вызов метода объекта по сравнению с вызовом процедуры и передачи объекта, проверка is по сравнению с добавлением Is_T_or_O в DrawList и подобное? Нужно ли всю эту красоту городить, если мне нужно максимально разогнать эту программу?

Автор: VisualProg 05.06.17, 07:21
Цитата
насколько вообще быстро работают переходы по ссылкам, вызов метода объекта по сравнению с вызовом процедуры и передачи объекта, проверка is по сравнению с добавлением Is_T_or_O в DrawList и подобное?

Достаточно быстро. У вас не должно быть с этим проблем.

Цитата
Нужно ли всю эту красоту городить, если мне нужно максимально разогнать эту программу?

Хотите гоняться за скоростью - пишите на TASM-е, если пишите в ООП, тогда и пишите, как вы выразились, красоту. Выбирать ООП и пытаться изворачиваться ради скорости это как забивать гвозди шуруповёртом...

Автор: Fr0sT 13.06.17, 08:31
Цитата VisualProg @
Хотите гоняться за скоростью - пишите на TASM-е, если пишите в ООП, тогда и пишите, как вы выразились, красоту. Выбирать ООП и пытаться изворачиваться ради скорости это как забивать гвозди шуруповёртом...

Очень бескомпромиссное суждение. Далеко не любой код на асме будет эффективнее того, что генерит компилятор. Оптимизировать на асме надо учиться, и довольно долго.
Цитата k.sovailo @
Можно ли тогда вопрос в догонку: а насколько вообще быстро работают переходы по ссылкам, вызов метода объекта по сравнению с вызовом процедуры и передачи объекта, проверка is по сравнению с добавлением Is_T_or_O в DrawList и подобное? Нужно ли всю эту красоту городить, если мне нужно максимально разогнать эту программу?

Надо смотреть по ситуации, абстрактной оптимизации не бывает. Надо искать узкие места и их менять. Смотри в сторону профайлера.
Как пример, по приведенному фраменту: is, насколько я помню, не очень эффективен. Плюс у тебя на каждую итерацию проверка полей, т.е. три if. Этого можно избежать разными способами:
1) как сказал Viktor - виртуальным методом, убираем as и значительно сокращаем основной цикл, однако добавляется переход по виртуальному методу CanDraw либо постоянный вызов Draw со встроенной проверкой - плюс читаемости, эффективность та же или даже в минусе
2) добавить поле ObjType к TF, где будут варианты: Empty - не рисовать, Text - тип TT, Sprite - тип TypO. Плюс понадобится отслеживать модификацию ключевых полей и соответственно изменение ObjType. Тогда цикл сведется к
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    case DrawList[i].ObjType of
      Text: TT(DrawList[i]).Draw;
      Sprite: TypO(DrawList[i]).Draw;
    end;

3) Разделить списки объектов по типам, что уберет проверку is.
4) Не добавлять в списки объекты, которые не будут отрисовываться, что уберет два if

Но прежде всего - надо смотреть, какие части кода выполняются часто и помногу. Глупо вылизывать то, что выполняется раз в час.

Автор: VisualProg 15.06.17, 07:43
Цитата Fr0sT @
Очень бескомпромиссное суждение. Далеко не любой код на асме будет эффективнее того, что генерит компилятор. Оптимизировать на асме надо учиться, и довольно долго.


Так, мы рассматривали вызов методов и "переходы по ссылкам":

Цитата k.sovailo @
а насколько вообще быстро работают переходы по ссылкам, вызов метода объекта по сравнению с вызовом процедуры и передачи объекта


Fr0sT, лично я не знаю как их ускорить средствами ООП. Поэтому и говорю что гнаться за такой мелочью - это идти в те самые дебри делфячного TASM-а. Вопрос правда встаёт, зачем было начинать писать на делфях, если надо считать настолько мелкие операции... А то что оптимизация зависит от знаний и умений ТС, имхо, это очевидно, так же как очевидно что если он плохо пишет на структурном уровне, его код будет ещё хуже и медленнее чем те же is/as в ООП.

Автор: leo 15.06.17, 11:58
Цитата k.sovailo @
а насколько вообще быстро работают переходы по ссылкам, вызов метода объекта по сравнению с вызовом процедуры и передачи объекта, проверка is по сравнению с добавлением Is_T_or_O в DrawList и подобное?

1) Не понятно, что имеется ввиду под "переходами по ссылкам"
2) На низком уровне вызов метода полностью эквивалентен вызову процедуры с передачей объекта (в первом неявном параметре)
3) Проверка по is довольно тормозная, поэтому использовать ее нужно по назначению, а именно проверять принадлежность объекта классу не в конце иерархии, а где-то в ее начале\глубине (когда другого выхода нет). А в твоем случае лучше использовать прямую проверку типа if DrawList[i].ClassType = TypO - по скорости это эквивалентно сравнению некого доп. поля ObjType с заданным значением, как предлагает Frost

Автор: Fr0sT 15.06.17, 15:01
Цитата VisualProg @
Так, мы рассматривали вызов методов и "переходы по ссылкам":

Вызов на Дельфи будет медленней разве что в случае виртуального метода, в остальном одинаково. Разве что действительно в том случае, когда неявный параметр объекта вытесняет параметр из регистра, и компилятор начинает использовать стек. Но это верно и по сравнению с обычной процедурой на Дельфи
Цитата leo @
if DrawList[i].ClassType = TypO

О! Здорово, что напомнил, я позабыл об этой фиче)

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)