Link to home
Start Free TrialLog in
Avatar of vladh
vladhFlag for Canada

asked on

Delphi object inheritance question

Hello experts;

Please see the code below. I am attempting to create two classes; one TANODE (ancestor) and one TDNODE (descendant). Objects of these type will be part of the taclass "container".  TACLASS has AddChild routine that adds these child nodes to container. It works fine on ancestor nodes. The problem comes when my descendant node is added to its container using AddChild routine, as marked in the code attached.
Could someone explain what am I doing wrong
Thanks
Vlad
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
 
type
  TForm1 = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
  tanode = class
    name : string;
    parent : ^tanode;
    public 
      constructor create;
      destructor destroy;
  end;
  
  tdnode = class(tanode)
    rdn : string;
    public 
      constructor create;
  end;
  
  taclass = class
      node  : tanode;
      desc : string;
      function addchild(parent: tanode):tanode;
    public 
      constructor create;
      destructor destroy;
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
 
constructor tanode.create;
begin
   inherited create;
end;
 
constructor tdnode.create;
begin
   inherited create;
end;
 
destructor tanode.destroy;
begin
   inherited destroy;
end;
 
 
constructor taclass.create;
begin
   inherited create;
end;
 
destructor taclass.destroy;
begin
   inherited destroy;
end;
 
function taclass.addchild(parent:tanode):tanode;
begin
   if parent=nil then
   begin
      result := tanode.create;
      result.name := 'root';
      result.parent:= nil;
   end else
   begin
      result := tanode.create;
      result.name := 'child_of_'+parent.name;
      result.parent := @parent;
   end;
end;
 
 
procedure TForm1.FormCreate(Sender: TObject);
var ac: taclass;
    an, an1 : tanode;
    dn, dn1 : tdnode;
begin
   ac := taclass.create;
   an := ac.addchild(nil);
   label1.Caption := an.name;
   an1 := ac.addchild(an);
   label2.Caption := an1.name;
   dn := tdnode(ac.addchild(nil));  <<<-----  when addchild returns, dn is "inaccessible value"
   dn.rdn := dn.name+'_RDN';
   label3.Caption := dn.rdn;
end;
 
end.

Open in new window

Avatar of twocandles
twocandles

There's a problem in your code: your casting your result to the descendant class here

   dn := tdnode(ac.addchild(nil));

but in the addchild method you're creating a tanode. Thus,

   dn.rdn := dn.name+'_RDN';

is not a valid statement because the result of addchild does not contain a rdn member.

I would change the function addChild to return a tdnode or, if you don't want to change that, create a tdnode instead of tanode (though you eventually return a tanode, but the cast won't be a problem)


My last suggestion is incomplete. If you're to cast the result to a tdnode, you MUST create a tdnode inside addchild function. If you want save from casting, then you should change the return to tdnode, but that's not mandatory in order for your code to work.
Avatar of vladh

ASKER

twocandles

Thanks for the suggestions. I guess I am a bit confused about the way inheritance works. My understanding was that TDNODE should inherit all that's contained in TANODE; and according to at least one Delphi book, I should be able to pass descendants to the routines designed to work on the ancestor nodes. I was expecting that AddChild routine would modify all fields common to both nodes, return TANODE which I would then recast into TDNODE and then simply change the RDN field (specific to TDNODE). It obviously is not working this way. Well then how can I take advantage of inheritance in this case? My understanding was that I can define the most common fields and routines in the top ancestor and just re-use them in descendants without rewriting - seems it's not the case?
ASKER CERTIFIED SOLUTION
Avatar of twocandles
twocandles

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Avatar of Geert G
Geert G
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of vladh

ASKER

twocandles,

it seems I should code a separate constructor for my descendant since I can use ascendants creator to return the descendant as the result. So it seems my descendant can not directly reuse the ascendants code for functions - or routines in ascendant class that return a result of type TANODE.
Looks like I have to rewrite constructors every time a descendant class is created. It's also a bit unclear at this point how I can reuse ascendants code for descendant class if I can not return the descendant class type from ancestors code (I am trying to make a generic, base ancestor class that would not be specifically aware of descendants but would still work for the common tasks such as add, delete, rename etc and the descendant would take care of that class-specific fields)

I wouldn't say I am new to Delphi, I've been using it for many years for quick and dirty programs to help with my job but I never took the time to learn OOP properly (used procedural approach for my code). I am trying to learn OOP now and that's where this question is coming from.

Geert,

Your suggestion is interesting although it doesn't answer my question (the questions purpose was not production code but rather learning of OOP).

I will need to experiment with the code a some more and will have to get back to this a bit later - time is a little tight right now.  Again, thank you for your time and suggestions.
Vlad
For the constructors, you don't have to override them (reprogram them) for each descendant unless you want extra behaviour for the descendant. For Delphi the constructor it's only a function that it's called when you create the instance. For example:

var
  ta: tanode;
  td: tdnode;
(...)
// You would have a tanode here. The tanode constructor would be called
ta := tanode.Create;

// You would have a tdnode here. The tdnode constructor
// would be called (if any), and then the tanode constructor (if you don't
// specify the inherited call, Delphi does it for you)
td := tdnode.Create;

// You would have a tdnode here. tdnode constructor would be called and then tanode constrcutor. But you would be able to access only tanode methods because of the
// use of a tanode variable. In this case you would be able to cast to tdnode because
// the right constructor is called
ta := tdnode.Create;

About creating descendant objects from an ancestor, it's not a Delphi "problem" but OOP phylosophy. An object can't use anything it's not aware of. I've come across this problem and Delphi provides a "workaround": using class of. Class keyword it's like a "reference to object" (not instance).

vladh
>>I am trying to learn OOP now and that's where this question is coming from.

I was trying to show a solution using OOP
This is also learning what allready exists and trying to use that.
Once you know what allready exists and how to use it, the next step is extending it
Avatar of vladh

ASKER

sorry for the delay - I will be evaluating the answer shortly
Thanks
Avatar of vladh

ASKER

I am closing the question manually and I do not wish for it to be deleted