• 0
ivadimos

Список объектов

Вопросы

Извиняюсь за псевдокод.

Есть класс (A), у него есть несколько дочерних(B1.B2...)
Создаю лист объектов TList<A>, в него добавляю объекты классов B1, B2...
 

Есть третий класс С, который имеет перегруженный метод для всех дочерних классов (A).

procedure p(obj: B1);

procedure p(obj B2);

Но объект в классе С всегда воспринимается как объект класса А.
Как я могу решить проблему?
 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

22 ответа на этот вопрос

  • 0

С - базовый класс для А?

Лучше бы примерчик кода, чтобы было понятно, кто кому и как и кем приходится.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

C - совершенно другой класс с перегруженным методом для объектов класса B1 B2 B3...

unit BaseComponent

type
  TBaseComponent = class Abstract (TLayout)
  protected
    LastColor: TAlphaColor;

    procedure SetColor(Value: TAlphaColor); virtual; abstract;
    function GetColor: TAlphaColor; virtual; abstract;

    function GetStrokeSize: Single; virtual;
    procedure SetStrokeSize(const Value: Single); virtual;

  public
    procedure Draw; virtual;
    procedure Click; override;
    procedure MouseMove(Shift: TShiftState; X, Y: Single); override;
    procedure DoMouseLeave; override;
    constructor Create(AOwner: TComponent); override;

  published
    property Color: TAlphaColor read GetColor write SetColor;
    property StrokeSize: Single read GetStrokeSize write SetStrokeSize;
  end;



unit CircleComponent

type
  TEllipseComponent = class(TBaseComponent)
  private
    Ellipse: TEllipse;

    procedure SetColor(Value: TAlphaColor); override;
    function GetColor: TAlphaColor; override;

    function GetStrokeSize: Single; override;
    procedure SetStrokeSize(const Value: Single); override;

  public
    constructor Create(AOwner: TComponent); override;
  end;

unit ShapeDrawer;

TEllipseComponent = class
public

  procedure Draw(Circle: TCircleComponent); overload;
  procedure Draw(Rectangle: TRectangleComponent); overload;
    constructor Create;
end;

есть List<TBaseComponent> в него свободно добавляются компоненты TCircleComponent, TRectangleComponent  и т.д.

tmpObject: TBaseComponent;

for tmpObject in List do
  ShapeDrawer .Draw(tmpObject); // всегда его считает как TBaseComponent. Хотя в листе лежат TRectangleComponent  и др.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
4 минуты назад, krapotkin сказал:

потому что надо писать override !

В каком же это месте?)  override переопределяет, а не перегружает метод.

Изменено пользователем ivadimos

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 1

компилятор не совсем дебильный и отличает

Draw(a:TClass1)
Draw(a:TClass2)
Draw(a:TClass3)

поэтому если мы пишем
TParentClass.Draw(obj:TClass1); virtual;
то должно быть
TAncestorClass.Draw(obj:TClass1); override;
а если другие параметры то никакого наследования уже и нет

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
9 минут назад, krapotkin сказал:

компилятор не совсем дебильный и отличает

Draw(a:TClass1)
Draw(a:TClass2)
Draw(a:TClass3)

поэтому если мы пишем
TParentClass.Draw(obj:TClass1); virtual;
то должно быть
TAncestorClass.Draw(obj:TClass1); override;
а если другие параметры то никакого наследования уже и нет

мы, видимо, недопонимаем друг друга.
Класс реализующий Draw - сторонний класс, и в иерархии классов он обособлен от других, но в качестве параметров принимает все объекты классов, которые являются дочерними классу TBaseComponent

Забудьте про метод Draw в TBaseComponent. это совершенно левый метод, остался после экспериментов

Изменено пользователем ivadimos

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

да я-то без проблем забуду, только 

tmpObject: TBaseComponent
ShapeDrawer .Draw(tmpObject)
я подозреваю, что внутри ShapeDrawer стоит tmpObject.Draw

у ф-ии есть параметр TBaseComponent. она вызывает у него Draw
если написать (tmpObject as TOtherObjcet).Draw будет вызываться другой Draw в точном соответствии с ООП

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
36 минуты назад, krapotkin сказал:

компилятор не совсем дебильный и отличает

Draw(a:TClass1)
Draw(a:TClass2)
Draw(a:TClass3)

поэтому если мы пишем
TParentClass.Draw(obj:TClass1); virtual;
то должно быть
TAncestorClass.Draw(obj:TClass1); override;
а если другие параметры то никакого наследования уже и нет

мы, видимо, недопонимаем друг друга.
Класс реализующий Draw - сторонний класс, и в иерархии классов он обособлен от других, но в качестве параметров принимает все объекты классов, которые являются дочерними классу TBaseComponent

unit Drawer;

interface

uses
  BaseComponent, RectangleComponent, CircleComponent, FMX.Dialogs;

type
  TDrawer = Class
    procedure Draw(RectangleComponent: TRectangleComponent); overload;
    procedure Draw(Cicrcle: TCircleComponent); overload;
  End;

implementation

{ TDrawer }

procedure TDrawer.Draw(Rectangle: TRectangleComponent);
begin
  ShowMessage('rect');
end;

procedure TDrawer.Draw(Cicrcle: TCircleComponent);
begin
  ShowMessage('сircle');
end;

end.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

krapotkin, вам правильно указал на счет компилятора и восприятия типов параметров.

Вы вызываете метод Draw и передаете туда по написанному тип TBaseComponent. Даже если там на самом деле TRectComponent или TEllipseComponent компилятор будет выбирать нужный метод по типу TBaseComponent. У вас просто архитектурная ошибка проектирования объектов и их взаимодействия.

  1. Либо вы передаете конечный класс (TRectComponent или TEllipseComponent) при вызове функции Draw. 
  2. Либо вы переделаете взаимодействие классов. Но это зависит от решаемой вами задачи.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
4 минуты назад, Brovin Yaroslav сказал:

krapotkin, вам правильно указал на счет компилятора и восприятия типов параметров.

Вы вызываете метод Draw и передаете туда по написанному тип TBaseComponent. Даже если там на самом деле TRectComponent или TEllipseComponent компилятор будет выбирать нужный метод по типу TBaseComponent. У вас просто архитектурная ошибка проектирования объектов и их взаимодействия.

  1. Либо вы передаете конечный класс (TRectComponent или TEllipseComponent) при вызове функции Draw. 
  2. Либо вы переделаете взаимодействие классов. Но это зависит от решаемой вами задачи.

об этом я написал в первом посте
Есть третий класс С, который имеет перегруженный метод для всех дочерних классов (A).

 

procedure p(obj: B1);
procedure p(obj B2);

Но объект в классе С всегда воспринимается как объект класса А.
Как я могу решить проблему?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Решение простое, при вызове метода p передавать объект явного класса, а не базового:

p(Obj1 as TB1);
p(Obj2 as TB2);

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
5 минут назад, Brovin Yaroslav сказал:

Решение простое, при вызове метода p передавать объект явного класса, а не базового:


p(Obj1 as TB1);
p(Obj2 as TB2);

 

логично, но как я могу перебирая лист определить тип без операторов is, if, case of?

В этом то и проблема)

Нужен какой-то лист, в который можно поместить объекты разных типов, а компилятор явно определяет его тип при передаче в перегруженный метод другого класса

Изменено пользователем ivadimos

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
Только что, ivadimos сказал:

логично, но как я могу перебирая лист определить тип без операторов is, if, case of?

В этом то и проблема)

На это я вам выше написал (пункт 2):

6 минут назад, Brovin Yaroslav сказал:

krapotkin, вам правильно указал на счет компилятора и восприятия типов параметров.

Вы вызываете метод Draw и передаете туда по написанному тип TBaseComponent. Даже если там на самом деле TRectComponent или TEllipseComponent компилятор будет выбирать нужный метод по типу TBaseComponent. У вас просто архитектурная ошибка проектирования объектов и их взаимодействия.

  1. Либо вы передаете конечный класс (TRectComponent или TEllipseComponent) при вызове функции Draw. 
  2. Либо вы переделаете взаимодействие классов. Но это зависит от решаемой вами задачи.

На первоначальный вопрос ответ дан. Чтобы ответить на новый вопрос, нужно определить чего вы хотите добиться в терминах конкретной задачи. А не просто кода.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
Только что, ivadimos сказал:

Нужен какой-то лист, в который можно поместить объекты разных типов, а компилятор явно определяет его тип при передаче в перегруженный метод другого класса

Так не будет. В такой постановке задачи, компилятор это за вас не сделает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Однако такие задачи решают обычно с использованием следующих шаблонов программирования:

  1. Интерпритатор
  2. Стратегия
  3. Шаблонный метод
  4. Посетитель

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
2 минуты назад, Brovin Yaroslav сказал:

На это я вам выше написал (пункт 2):

На первоначальный вопрос ответ дан. Чтобы ответить на новый вопрос, нужно определить чего вы хотите добиться в терминах конкретной задачи. А не просто кода.

Нужно создать список с объектами различных классов.
Внешний модуль принимает элемент списка в перегруженный метод.
Без использования  операторов is, if, case of.
Метод делает что угодно, допустим выводит сообщение.
Я, естественно, понимаю, что проще и изящнее будет реализовать метод draw внутри каждого класса, но стоит другая задача.
Мне просто интересно можно так сделать или нет? 

//в С#  это решается через dynamic переменные.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

создание метода Draw у класса это архитектурная ошибка. тогда очень сильно нарушается инкапсуляция. юниты становятся завязаны друг на друга.

должен быть некий суперобъект-рисователь, который с помощью if case или еще чего будет запускать рисование по ДАННЫМ, которые хранятся в объектах. ТОЛЬКО ДАННЫЕ
я над такими задачами просидел довольно долго, и это решение получается наилучшим...

p.s.

Cxx vs Delphi - механизмы ООП у них одинаковы, поэтому хоть трижды dynamic, результат будет абсолютно тот же
даже если представить мифический код, который будет в листе запускать у каждого свой Draw, реализация у него будет абсолютно такая же - перебор по IF. Выигрыша - 0.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
2 часа назад, krapotkin сказал:

создание метода Draw у класса это архитектурная ошибка. тогда очень сильно нарушается инкапсуляция. юниты становятся завязаны друг на друга.

должен быть некий суперобъект-рисователь, который с помощью if case или еще чего будет запускать рисование по ДАННЫМ, которые хранятся в объектах. ТОЛЬКО ДАННЫЕ
я над такими задачами просидел довольно долго, и это решение получается наилучшим...

p.s.

Cxx vs Delphi - механизмы ООП у них одинаковы, поэтому хоть трижды dynamic, результат будет абсолютно тот же
даже если представить мифический код, который будет в листе запускать у каждого свой Draw, реализация у него будет абсолютно такая же - перебор по IF. Выигрыша - 0.

Я это прекрасно понимаю, просто заинтересовало, есть ли такая возможность: перегрузить метод внешнего класса, который определит, что передаю я туда потомка класса. Хотя лист объявлен как лист объектов родительского класса.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Я этот механизм реализовывал следующим способом:

В родительском классе 2 метода - public Draw и protected DoDraw; virtual; abstract;

В родительском Draw вызывается DoDraw

В наследниках остается переопределить только DoDraw и все прекрасно работает без  is, as, if, case of.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
37 минуты назад, dnekrasov сказал:

Я этот механизм реализовывал следующим способом:

В родительском классе 2 метода - public Draw и protected DoDraw; virtual; abstract;

В родительском Draw вызывается DoDraw

В наследниках остается переопределить только DoDraw и все прекрасно работает без  is, as, if, case of.

Почти по такому принципу у меня сейчас и работает.
У меня есть метод draw у родителя и я его просто переопределил для каждого наследника. НО.

А если задача стоит так, что компоненты не содержат метода отрисовки?
Они отрисовываются внешним классом, который имеет перегруженный метод, принимающий объекты.
Внешний класс не имеет никакого отношения к этой иерархии классов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
2 минуты назад, ivadimos сказал:

А если задача стоит так, что компоненты не содержат метода отрисовки?
Они отрисовываются внешним классом, который имеет перегруженный метод, принимающий объекты.
Внешний класс не имеет никакого отношения к этой иерархии классов.

Попробуйте передавать в этот перегруженный метод не просто (B1) а (B1 as B1.ClassType)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
56 минут назад, dnekrasov сказал:

Попробуйте передавать в этот перегруженный метод не просто (B1) а (B1 as B1.ClassType)

Думаете не пробовал? первым делом, но компилятор ругает, что все равно явно не знает какой там тип

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти

  • Похожий контент

    • От Alex7wrt
      Добрый день
      Столкнулся с такой задачей:
      Существует несколько TObjectList<TRectangle>. При создании каждого из них  AOwnsObjects задано как True. 
      RectList[i]:=TObjectList<TRectangle>.Create(True); ............ Nrect:=TRectangle.Create(nil); Nrect.Parent:=Form1; RectList[i].Add(Nrect); Существует ли способ определить, к какому из  TObjectList принадлежит элемент? 
    • От Anasazi
      Всем привет. Чиатю книгу Осипова по Delphi. В книге есть один пример создания простой игры, вроде бы в нем все понятно кроме одного момента.
      По задумке один таймер должен создавать шарики и присваивать им случайные координаты. Второй таймер должен опускать шарики вниз.
      С первым все просто и понятно понятно:
      procedure TForm1.Timer1Timer(Sender: TObject); Var Circle:Tcircle; begin Randomize; Circle:=TCircle.Create(nil); List.Add(circle); Circle.Parent:=Form1; Circle.Position.Y:=0; Circle.Position.X:=Random(Round(Form1.Width-Circle.Width)); end; А вот со вторым какие-то чудеса:
      procedure TForm1.Timer2Timer(Sender: TObject); var i: integer; begin For i:=0 to List.Count-1 do with List.Items do begin Position.Y:=Position.Y+0.5; Я не могу понять почему при перемещении шариков вниз мы пишем просто Position.Y:=Position.Y+0.5;
      Как компилятор понимает что это Position.Y именно шарика, а не формы допустим или квадрата?
      Я привык обращаться к свойству Position так Rectengle1.Position.Y.
      Подскажите пожалуйста как так.
    • От PFC
      Есть класс:
      class TMyObjectInt : public TObject { private: int Value; public: __fastcall TMyObjectInt (int _Value); __fastcall ~TMyObjectInt(); }; Есть ComboBox:
      for (int i = 1; i <= 10; i++) cbxItems->Items->AddObject(IntToStr(i), new TMyObjectInt(i)); Почему при вызове ->Clear() не вызываются деструкторы для TObject'ов? Казалось бы базовый TObject замечательно для этого подходит, даже деструктор у него виртуальным объявлен.
      Я могу написать свою функцию очистки, но это потенциально приводит к ошибкам, особенно если я отдаю указатель на ComboBox за пределы своей формы.
       
      Кстати, так же ведут себя и многие другие контролы, да и в VCL было так же.
  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу