PDA

View Full Version : Inheriting Classes? *solved*



FusionWolf
06-02-2005, 08:33 PM
If I have a class which has a method which takes a second class as an parameter. And then I have third class which is inherited from second, so it contains same methods as second one does. Now if I want to pass that third class to the method of the first class. How to implement that method so it accepts a class and all it's descendants?

for example...




TFirstClass = Class
private
public
procedure Dosomething(AClass: ???????); // How to do this so it accepts
end; //the second and all its descentand classes?


TSecondClass = Class
private
public
procedure DoSomething;
end;


TThirdClass = Class(TSecondClass) // Notice inheritance.
private
public
procedure DoSomethingElse;
end;

var
FirstClass: TFirstClass;
3rdClass: TThirdClass;

.
.
.

procedure SomeProcedureSomewhere;
begin
FirstClass.DoSomething(3rdClass);
end;


How to implement something like that?

Clootie
06-02-2005, 09:27 PM
type

TSecondClass = Class;

TFirstClass = Class
private
public
procedure Dosomething(AClass: TSecondClass); // How to do this so it accepts
end; //the second and all its descentand classes?


TSecondClass = Class
private
public
procedure DoSomething;
end;


TThirdClass = Class(TSecondClass) // Notice inheritance.
private
public
procedure DoSomethingElse;
end;

FusionWolf
07-02-2005, 01:52 AM
Thank you Clootie for your answer but I forgot to mention that firstclass DoSomething() method initializes that class which is passed in method. So it should be var parameter.

Like this....




TFirstClass = Class
private
public
procedure Dosomething(var AClass: ???????); // How to do this so it accepts
end; //the second and all its descentand classes?


TSecondClass = Class
private
public
procedure DoSomething;
end;


TThirdClass = Class(TSecondClass) // Notice inheritance.
private
public
procedure DoSomethingElse;
end;

var
FirstClass: TFirstClass;
3rdClass: TThirdClass;


procedure TFirstClass.DoSomething(var AClass: ?????????);
begin
AClass := ???????(AClass).create; // or something like that??
end;

.
.
.

procedure SomeProcedureSomewhere;
begin
FirstClass.DoSomething(3rdClass);
end;


Or how I should do something like this. All I get now is compiler error: "Types of actual and formal var parameters must be identical." at the line inside SomeProcedureSomewhere().

Edit: please help me with this. This is very important for my game engine.

{MSX}
07-02-2005, 07:54 AM
You cannot do that as you said. If you use the var parameter, the types must be the same. (and it's obvious, since otherway you could break the type system, putting a SecondClass instance in a ThirdClass object)

If you need to initialize the class, you should do something like:

function Dosomething():TSecondClass;

where you can return whatever descendant of TSecondClass.

FusionWolf
07-02-2005, 08:13 AM
You cannot do that as you said. If you use the var parameter, the types must be the same. (and it's obvious, since otherway you could break the type system, putting a SecondClass instance in a ThirdClass object)

If you need to initialize the class, you should do something like:

function Dosomething():TSecondClass;

where you can return whatever descendant of TSecondClass.

Okey, thanks. But does that execute constructor of descentand? I mean if i introduce a constructor in the TThirdClass and then try to create instance of it with that DoSomething function which returns instance of TSecondClass?

edit: what I try to do is this -> http://tonyandpaige.com/tutorials/game1.html


edit2: I just build up a little test program which works fine except that constructor of descentant classes is never ran. Here's the code.

This is in own unit file....


unit uGameStates;

interface

uses
Classes;

type
TStateClass = Class
public
constructor Create;
destructor Destroy; override;
end;

type
TManagerClass = Class(Tlist)
public
procedure ChangeState(State: TStateClass);
constructor Create;
destructor Destroy; override;
end;

implementation

{ TStateClass }

constructor TStateClass.Create;
begin

end;

destructor TStateClass.Destroy;
begin

inherited;
end;

{ TManagerClass }

constructor TManagerClass.Create;
begin
inherited Create;
end;

destructor TManagerClass.Destroy;
var
Index: Integer;
begin
for Index := 0 to Pred(Count) do begin
TStateClass(Items[0]).Free;
Delete(0);
end;
inherited;
end;

procedure TManagerClass.ChangeState(State: TStateClass);
begin
if Self.Count > 0 then begin
TStateClass(Items[0]).Free;
Delete(0);
end;
State := TStateClass.Create;
Self.Add(State);
end;
end.


And here's the main unit (a form with a button)....


unit uMain;

interface

uses
Forms, Classes, Controls, StdCtrls, uGameStates;

type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
public
end;

type
TLevel1 = class(TStateClass)
private
FOldCapt: String;
public
constructor create;
destructor destroy; override;
end;

var
Form1: TForm1;
StateManager: TManagerClass;
Level1: TLevel1;

implementation
{$R *.dfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
StateManager := TManagerClass.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
StateManager.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
StateManager.ChangeState(Level1);
end;

{ TLevel1 }

constructor TLevel1.create;
begin
inherited Create;
FOldCapt := Form1.Caption;
Form1.Caption := 'Yes, it''s works!';
end;

destructor TLevel1.destroy;
begin
Form1.Caption := FOldCapt;
inherited;
end;
end.


When button on the form is pressed StateManager.ChangeState() method is ran with Level1 as an parameter. Then ChangeState() method should ran constructor of TLevel1 and store class to TList (manager itself). And when manager changes state again (not in this example) previous state is freed and new state stored in manager. So, then TLevel1.Destroy() method should be called.

And like I said works fine except TLevel1.Constructor is never ran. How to do this right?

{MSX}
07-02-2005, 10:19 AM
Umm your code is wrong in various place :D
First, it never calls TLevel constructor becouse you never call TLevel.create.
You call TStateClass.create, which creates only TStateClass.
Also, if you pass an object to a procedure without the var clausule, and you reassign the parameter with a new object, the new object is not avaiable on the caller side. So your TLevel1 is unassigned at the Button1Click, and the TStateClass created is lost (and you have a memory leak).

Clootie
07-02-2005, 10:45 AM
TStateClass = Class
public
constructor Create; virtual;
destructor Destroy; override;
end;

TClassOfState = class of TStateClass;

TLevel1 = class(TStateClass)
private
FOldCapt: String;
public
constructor create; override;
destructor destroy; override;
end;

procedure TManagerClass.ChangeState(out State: TStateClass; StateClass: TClassOfState);
begin
....
State := StateClass.Create;
Self.Add(State);
end;

FusionWolf
07-02-2005, 11:27 AM
Umm your code is wrong in various place :D
First, it never calls TLevel constructor beco...<snip>

Yea, yea, yea I know, but you ain't helping me either :?


CLOOTIE! You're the best! It works now. Thank you SOOOOOOO much! Grab a cup of coffee and lean back. I'll treat ;).

edit: Here is the final code of this simple example of mine. If someone is doing something similar.....

Unit uStateManager.

unit uStateManager;

interface

uses
Classes;

type
TStateClass = Class
public
constructor Create; virtual;
destructor Destroy; override;
end;

TClassOfState = Class of TStateClass;

type
TManagerClass = Class(Tlist)
public
procedure ChangeState(out State: TStateClass; StateClass: TClassOfState);
constructor Create;
destructor Destroy; override;
end;

implementation

{ TStateClass }

constructor TStateClass.Create;
begin
end;

destructor TStateClass.Destroy;
begin
inherited;
end;

{ TManagerClass }

constructor TManagerClass.Create;
begin
inherited Create;
end;

destructor TManagerClass.Destroy;
var
Index: Integer;
begin
for Index := 0 to Pred(Count) do begin
TStateClass(Items[0]).Free;
Delete(0);
end;
inherited;
end;

procedure TManagerClass.ChangeState(out State: TStateClass; StateClass: TClassOfState);
begin
if Self.Count > 0 then begin
TStateClass(Items[0]).Free;
Delete(0);
end;
State := StateClass.Create;
Self.Add(State);
end;
end.


Main module.

unit uMain;

interface

uses
Forms, Classes, Controls, StdCtrls, uStateManager;

type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
public
end;

type
TLevel1 = class(TStateClass)
private
FOldCapt: String;
public
constructor create; override;
destructor destroy; override;
end;

type
TLevel2 = Class(TLevel1)
public
constructor create; override;
end;

var
Form1: TForm1;
StateManager: TManagerClass;
Level1: TStateClass;
Level2: TStateClass;

implementation
{$R *.dfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
StateManager := TManagerClass.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
StateManager.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
StateManager.ChangeState(Level1, TLevel1);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
StateManager.ChangeState(Level2, TLevel2);
end;

{ TLevel1 }

constructor TLevel1.create;
begin
FOldCapt := Form1.Caption;
Form1.Caption := 'Level1 Loaded...';
end;

destructor TLevel1.destroy;
begin
Form1.Caption := FOldCapt;
inherited;
end;

{ TLevel2 }

constructor TLevel2.create;
begin
FOldCapt := Form1.Caption;
Form1.Caption := 'Level2 Loaded...';
end;
end.