Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Copy-Konstruktor


Geri - Mi 12.08.09 11:31
Titel: Copy-Konstruktor
Hallo zusammen


Ich habe mehrere Klassen, welche von einander abgeleitet sind. Jede der Klasse wurde mit einem Copy-Constructor ausgesattet.
Nun habe ich in einer Liste Instanzen der verschiedenen Klassen und möchte eine Kopie der gesamten Liste erstellen


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:
type
    TBaseClass = class
               mValue:Integer;
               constructor.create(aValue);overload;
               constructor.Create(aINstance:TBaseClass);overload;  // copy Konstruktor
            end;

    TClassB = class(TBaseClass)
               constructor.Create(aInstance:TBaseClass);overload// copy Konstruktor
            end;

    TClassC = class(TBaseClass)
               constructor.Create(aInstance:TBaseClass);overload;  // copy Konstruktor
            end;
   
var BaseList:Tlist;
    i:Integer;
    copyList:Tlist;
    Element:TBaseClass;
begin
  BaseList:=TList.Create();
  BaseList.add(TBaseClass.Create(123));
  BaseList.add(TClassB.Create(456));
  BaseList.add(TClassC.Create(789));
  ..

// und nun die Kopie der gesamten Liste erstellen
  CopyList:=TList.Create();
  for i:= 0 to BaseList.Count-1 do
  Begin
    Element:=BaseList[i]
    if Element.classtype = TBaseClass then CopyList.add(TBaseClass.Create(Element)) else
    if Element.classtype = TClassB then CopyList.add(TBaseB.Create(Element)) else
    if Element.classtype = TClassC then CopyList.add(TBaseC.Create(Element)) else
    Begin
      // error, unkown class
    end;
  end
End;


Gibt es vielleicht eine Möglichekeit die Abfrage der gesamten Klassen zu vermeiden und gleich den richtigen Constructor aufzurufen?

Etwas ähnliches wie vielleicht:

Delphi-Quelltext
1:
   CopyList.Add((Element.ClassType).Creat(Element)) ???                    

Fehlen mir evtl. in den Konstruktoren nur ein paar overrides :)?

Vielen Dank für ein paar hilfreiche Tipps im Voraus

Geri


Xentar - Mi 12.08.09 11:39

Ich versteh noch nicht, was du machen willst, aber ich rate mal

Delphi-Quelltext
1:
2:
3:
4:
for i := 0 to BaseList.Count - 1 do
begin
  CopyList.Add(BaseList[i]);
end;


Und was meinst du bitte mit "Copy-Konstruktor"?? Eine Klasse hat einen einzigen Konstruktor.. da wird nix gecopy'd. Was du meinen könntest, wäre Assign() ?


jaenicke - Mi 12.08.09 11:41

Was ein Konstruktor mit Copy zu tun hat, verstehe ich jetzt nicht? Meinst du den Klassentyp "kopieren", also ein entsprechendes Objekt neu erstellen? Oder meinst du den Inhalt kopieren? Dann wäre Assign geeigneter.

Eine Möglichkeit wäre das Factory-Pattern [http://en.wikipedia.org/wiki/Factory_method_pattern]. ;-)

Du kannst das zum Beispiel so machen, dass du eine ID übergibst, du kannst auch via class of und ggf. TClassList (Unit Contnrs) eine Liste von Klassen verwalten. Das geht z.B. so, dass sich die Klassen selbst praktisch anmelden in dieser Liste, oder dass diese Liste in initialization gefüllt und in finalization wieder freigegeben wird. Dann kannst du das auch in einer Schleife machen.


BenBE - Mi 12.08.09 11:43

Copy-Constructors kommen aus der C++-Welt (was richtig nerviges dort). Etwas analoges unter Delphi erhälst Du mit Assign; vorausgesetzt, Assign ist für deine Klassen korrekt implementiert.

Zu deiner Frage, warum der Source nicht geht: Weil TClass die geforderte Methode nicht bereitstellt; da fehlt mindestens noch ein Typecast auf class of TBaseClass.


Reinhard Kern - Mi 12.08.09 14:01

user profile iconXentar hat folgendes geschrieben Zum zitierten Posting springen:

Und was meinst du bitte mit "Copy-Konstruktor"?? Eine Klasse hat einen einzigen Konstruktor..


Hallo,

ist mir noch nicht aufgefallen - eine Klasse von mir hat 9 Konstruktoren (einen für jeden verwalteten Datentyp) und funktioniert einwandfrei.

Und ein Konstruktor kann machen was er will, auch eine Kopie von einem anderen Objekt.

Gruss Reinhard


Geri - Mi 12.08.09 14:21

Hallo zusammen

Vielen Dank für eure Tipps.

Unter einem "Copy-Constructor" verstehe ich einen Constructor, welcher als Parameter eine Referenz seiner BasisKlasse hat. Wird er aufgerufen, dann erzeugt er eine neue Instanz der Klasse und weist sich anschliessend selbst die Werte von aInstance zu.

z.B.

Delphi-Quelltext
1:
2:
3:
Type
   TCLASSA = class
              constructor Create(aInstance:TCLASSA);



Ich möchte eine "echte" Kopie des Objektes erstellen. Analoger code findet man in Zeile 32 bis 34. Diese Methode funktioniert sehr gut, ist für den Fall des Kopierens aber etwas umständlich.

Schön wäre eben wenn man nicht zuerst herausfinden müsste um welche Klasse essich handelt:)

Beste Grüsse

Geri


BenBE - Mi 12.08.09 14:28

Kopie erstellen über Assign realisieren. Oder mit Factory-Pattern kombinieren.

Alternativ statische Methode Clone anlegen und überall überschreiben (in den abgeleiteten Klassen)


Tilman - Mi 12.08.09 15:32

user profile iconXentar hat folgendes geschrieben Zum zitierten Posting springen:
Eine Klasse hat einen einzigen Konstruktor.. da wird nix gecopy'd.


Mehrere Konstruktoren sind in Delphi ganz normal. Z.B. haben alle von TWinControl abgeleiteten Komponenten in der VCL schonmal mindestens 2, nämlich Create und CreateParented.


Geri - Mi 12.08.09 15:54

@BenBe:
Wäre es nicht ein Stilbruch wenn man in einer Assign-Methode eine Instanz der entsprechenden Klasse erzeugen würde? Meiner Ansicht nach hat Assign doch lediglich die Aufgabe Werte zuzuweisen sondern nicht ein neues Objekt anzulegen...

Die Sache mit den Patterns habe ich mir noch nicht angeschaut....:)

Mit dem Hinweis von BenBe bin ich nun aber zu einer meiner nach "schönen" Lösung gekommen. Vielleicht auch für andere interessant.
Anbei der Code. Kritik und Lösungsalternativen werden gerne entgegengenommen.

Im Beispiel werden drei Klassen implementiert und für jede Klasse eine statische Clone-Methode. Es wird je ein Objekt der Klassen TBaseClass, TClassB und TClassC erzeugt und in einer Liste abgelegt. Anschliessend wird eine Clone der Liste (inkl. der abgelegeten Objekte) erstellt.


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:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
    TBaseClass = class
                  private
                    mValue:Integer;
                  public
                    constructor create(aValue:Integer);overload;
                    class Function clone(aInstance:TBaseClass):TBaseClass;virtual;
                  published
                    property Value:Integer read mValue write mValue;
                  end;

    TClassB = class(TBaseClass)
                    class Function clone(aInstance:TBaseClass):TBaseClass;override;
                  end;

    TClassC = class(TBaseClass)
                    class Function clone(aInstance:TBaseClass):TBaseClass;override;
                  end;


var
  Form1: TForm1;

implementation

{$R *.dfm}

constructor TBaseClass.Create(aValue:Integer);
Begin
  mValue:=aValue;
End;

class Function TBaseClass.Clone(aInstance:TBaseClass):TBaseClass;
Begin
  result:=TBaseClass.create(aInstance.mValue);
End;

class Function TClassB.Clone(aInstance:TBaseClass):TBaseClass;
Begin
  result:=self.create(aInstance.mValue);
End;

class Function TClassC.Clone(aInstance:TBaseClass):TBaseClass;
Begin
  result:=self.create(aInstance.mValue);
End;


procedure TForm1.Button1Click(Sender: TObject);
var BaseList:TList;
    CopyList:TList;
    i:Integer;
    Element:TBaseClass;
begin
  BaseList:=TList.Create();
  BaseList.add(TBaseClass.Create(123));
  BaseList.add(TClassB.Create(456));
  BaseList.add(TClassC.Create(789));
  CopyList:=TList.Create();
  for i:= 0 to BaseList.Count-1 do
  Begin
    Element:=BaseList[i];
    CopyList.Add(Element.clone(Element));
  end;

  for i:= 0 to copyList.Count - 1 do
  Begin
    Element:=CopyList[i];
    memo1.lines.add('Copy of class : ' + Element.ClassName + '   value ' + IntToStr(Element.mValue));
  End;

  while BaseList.count > 0 do
  begin
    Element:=BaseList[0];
    Element.Destroy();
    BaseList.delete(0);
  end;
  BaseList.Destroy();

  while CopyList.count > 0 do
  begin
    Element:=CopyList[0];
    Element.Destroy();
    CopyList.Delete(0);
  end;
  CopyList.Destroy();}
end;


Beste Grüsse

Geri