Entwickler-Ecke

Sonstiges (Delphi) - Lazarus Vererbung virtual abstract override


glotzer - So 23.06.13 11:13
Titel: Lazarus Vererbung virtual abstract override
Hallo,

Ich hab ein Problem mit der Vererbung in Lazarus,
ich bin jetzt seit 2 Tagen am rumprobieren und finde einfach keine Lösung.
Google kann mir leider auch nicht helfen (wahrscheinlich verwende ich die falschen Suchbegriffe)

Mein Problem ist eigentlich sehr einfach, ich habe 3 Klassen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
type
  TA = class
    public
      procedure x; virtualabstract;
  end;

  TB = class(TA)
    public
      procedure x; override;
  end;

  TC = class(TB)
    public
      procedure m;
  end;

  D = class of TA;


Wenn ich jetzt x so aufrufe:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
var
  obj: TA;
  Objclass: D;
begin
  Objclass := TC;
  obj := Objclass.Create;
  obj.x;
  obj.Free;
end;

kommt der Fehler x sei abstract und nicht implementiert (genauer :
Zitat:
RunError(211)
)

Ich kann in der Klasse TB auch keine Haltepunkte setzen, es scheint als würde der Compiler die Klasse ignorieren.
Das abschalten der Optimierung hat auch nicht geholfen.
Ich vermute es liegt irgendwie an der Klassenreferenz...

Vieleicht hat ja hier jemand eine tolle Idee, würd mich freuen :D

Grüße
Glotzer


jaenicke - So 23.06.13 11:47

Ich glaube du meinst eher:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
var
  obj: TA;
  Objclass: D;
begin
  Objclass := TC;
  obj := Objclass.Create;
  obj.x;
  obj.Free;
end;


glotzer - So 23.06.13 11:49

ups ja, danke.
Allerdings nur hier ein kleiner Tippfehler, im eigentlichen Programm ist es schon richtig.


jaenicke - So 23.06.13 13:50

Dann hast du wohl eine alte Version von Lazarus. In Version 1.0.10 funktioniert das problemlos, genau wie in Delphi.


glotzer - So 23.06.13 13:55

hmm ich verwende 1.0.8.

Danke für deine Mühe ich versuchs mal mit nem Update.


Tranx - So 23.06.13 14:26

Entschuldige, aber Du definierst ja auch obj als TA:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
var
  obj: TA;  <=== Objekt hat TA-Klasse
  Objclass: D;
begin
  Objclass := TC;
  obj := Objclass.Create;
  obj.x;
  obj.Free;
end;


wie wäre es denn, wenn Du das schreibst:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
var
  obj: TC;  // korrekte Klassenzuordnung
//  Objclass: D;
begin
//  Objclass := TC;
  obj := TC.Create;
  obj.x;
  obj.Free;
end;


Ich benutze als einer Komponente TStringgrid ja auch nicht TCustomGrid als Klassenzuordnung für die Komponente sondern eben TStringgrid.


Martok - So 23.06.13 14:26

Mich wundert grade etwas, dass das wieder geht :)
Eigentlich war das mal sehr lange so, dass der Konstruktor einer Klasse, die per Klassenreferenz erzeugt wird, virtuell sein muss. Im Beispiel würde sonst immer TA.Create aufgerufen werden, weil die Referenz da hin zeigt.

Übrigens ist Laz 1.1 und FPC 2.7.1 aktuell. Sollte man mal updaten (ggf. auch auf Snapshots). Man kennt das heute gar nicht mehr, aber bei denen wird wirklich aktiv entwickelt, da sind 4 Wochen nicht aktualisiert eine halbe Welt.


glotzer - So 23.06.13 14:42

Hab grad noch keine Zeit gehabt zu Updaten, aber:



user profile iconTranx hat folgendes geschrieben Zum zitierten Posting springen:

wie wäre es denn, wenn Du das schreibst:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
var
  obj: TC;  // korrekte Klassenzuordnung
//  Objclass: D;
begin
//  Objclass := TC;
  obj := TC.Create;
  obj.x;
  obj.Free;
end;


Ich benutze als einer Komponente TStringgrid ja auch nicht TCustomGrid als Klassenzuordnung für die Komponente sondern eben TStringgrid.


Würd ich ja gerne aber kann ich nicht, sonst könnte ich mir die komplette Nummer mit der Klassenreferenz sparen.
Es geht darum, dass abhängig von einer Usereingabe Objekte verschiedener Klassen erzeugt werden, welche sich vorher Registriert haben. Ich weiß zur Designzeit nur, dass sie alle von der selben Klasse abgeleitet sind.



user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Mich wundert grade etwas, dass das wieder geht :)
Eigentlich war das mal sehr lange so, dass der Konstruktor einer Klasse, die per Klassenreferenz erzeugt wird, virtuell sein muss. Im Beispiel würde sonst immer TA.Create aufgerufen werden, weil die Referenz da hin zeigt.

Er ist virtuell.



user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:

Übrigens ist Laz 1.1 und FPC 2.7.1 aktuell. Sollte man mal updaten (ggf. auch auf Snapshots). Man kennt das heute gar nicht mehr, aber bei denen wird wirklich aktiv entwickelt, da sind 4 Wochen nicht aktualisiert eine halbe Welt.

Wird gemacht, danke.


jaenicke - So 23.06.13 14:46

user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Eigentlich war das mal sehr lange so, dass der Konstruktor einer Klasse, die per Klassenreferenz erzeugt wird, virtuell sein muss. Im Beispiel würde sonst immer TA.Create aufgerufen werden, weil die Referenz da hin zeigt.
Das ist auch so, aber wenn man z.B. gar keinen Konstruktor in TB hat, ist es ja korrekt, dass der des Vorfahren aufgerufen wird.

Wenn man natürlich einen in der abgeleiteten Klasse definiert, kann man den der Elternklasse nicht überschreiben, wenn der nicht als virtuell definiert ist.


glotzer - So 23.06.13 14:55

Gerade geupdated, keine Änderung, weiterhin das selbe Problem.
Ich versuch mal ein möglichst kleines Testprojekt zu machen.


jaenicke - So 23.06.13 15:30

Ein Testprojekt ist natürlich das beste, aber du kannst auch erstmal die konkreten Deklarationen posten, die du im echten Projekt benutzt, die müssen ja so nicht kompilierbar sein.


glotzer - So 23.06.13 16:47

Ich poste hier jetzt nur das entscheidende, ein komplettes Projekt ist im Anhang, es (sollte) auch kompilierbar sein.

main:

Delphi-Quelltext
1:
2:
3:
4:
5:
var
  GUI: TBasicGUI; 
begin
  GUI := CreateGUISystem(GUI_Systems.Items[GUISelBox.ItemIndex]);
end;


basicGUI:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
type
  { TBasicGUI }

  TBasicGUI = class(TStringList)
    public
      constructor Create; virtual;
      destructor Destroy; override;
  end;

type
  TBasicGUIClass = class of TBasicGUI;
  TBasicGUIList = specialize TFPGList<TBasicGUIClass>;

var
  GUI_Systems: TBasicGUIList;

function CreateGUISystem(ASystem: TBasicGUIClass): TBasicGUI;
procedure RegisterGUISystem(ASystem: TBasicGUIClass);

implementation

function CreateGUISystem(ASystem: TBasicGUIClass): TBasicGUI;
begin
  result := ASystem.create;
end;

procedure RegisterGUISystem(ASystem: TBasicGUIClass);
begin
  GUI_Systems.Add(ASystem);
end;

constructor TBasicGUI.Create;
begin
  inherited Create;
  showmessage('a');
end;

destructor TBasicGUI.Destroy;
begin
  inherited Destroy;
end;    

initialization
  GUI_Systems := TBasicGUIList.Create;

finalization
  GUI_Systems.Free;


basicguiform:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
type
  { TBasicFormGUI }

  TBasicFormGUI = class(TBasicGUI)
    public
      constructor Create; override;
      destructor Destroy; override;
  end;

  {$R *.lfm}

implementation

{ TBasicFormGUI }

constructor TBasicFormGUI.Create;
begin
  inherited Create;
  showmessage('b');
end;

destructor TBasicFormGUI.Destroy;
begin
  inherited Destroy;
end;


gui_Tiatai:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
type
  { TTiatai }
  TTiatai = class(TBasicFormGUI)
  public
    a: integer;
  end;

implementation


{ TTiatai }

initialization
  RegisterGUISystem(TTiatai);


Problem ist, dass nur "a" angezeigt wird, niemals "b". Es sollte beides angezeigt werden.


jaenicke - So 23.06.13 18:14

Du hast TBasicFormGUI doppelt deklariert...
Das mit dem Showmessage b aus basicguiform.pas wird nie benutzt, sondern das aus basicformgui.pas.


glotzer - So 23.06.13 18:18

WOW, da muss man erstmal drauf kommen! vielen vielen Dank! Ich wär da wohl noch ewig davor gesessen ^^