Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Container Klasse Speicherverletzung
Dude566 - Mo 16.11.09 20:57
Titel: Container Klasse Speicherverletzung
Also wir haben in der Schule jetzt mal mit OOP angefangen, ich komme nicht so ganz mit den Unterrichtsmethoden zurecht (Chaos).
Ich hab eine Container Klasse und eine Klasse TRing, doch ich bekomme eine Zugriffsverletzung wenn ich TRing in TContainer erstellen möchte.
TContainer
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:
| unit uTContainer;
interface
uses uTRing, Graphics;
type TContainer = class(TObject) private Item : array [1..5] of TRing; public constructor Create; destructor Free; procedure Init;
end;
implementation
constructor TContainer.Create; begin inherited Create; Init; end;
destructor TContainer.Free; var i : integer; begin for i := 0 to 4 do Item[i].Free; end;
procedure TContainer.Init; var i : integer; begin for i := 0 to 4 do Item[i] := TRing.Create; end;
end. |
TRing
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:
| unit uTRing;
interface
uses Graphics;
type TRing = class(TObject) private stift : integer; farbe : TColor; x1,y1,x2,y2 : integer; public constructor Create; destructor Free; procedure Init; procedure SetStaerke(dicke : integer); function GetStaerke : integer; procedure SetFarbe (stiftfarbe : TColor); function GetFarbe : TColor; procedure SetPosition(ObenLinksX,ObenLinksY,UntenRechtsX,UntenRechtsY : integer); procedure Zeichnen (myCanvas : TCanvas); end;
implementation
procedure TRing.SetStaerke(dicke : integer); begin stift := dicke; end;
function TRing.GetStaerke : integer; begin result := stift; end;
procedure TRing.SetFarbe (stiftfarbe : TColor); begin farbe := stiftfarbe; end;
function TRing.GetFarbe : TColor; begin result := stift; end;
procedure TRing.SetPosition(ObenLinksX,ObenLinksY,UntenRechtsX,UntenRechtsY : integer); begin x1 := ObenLinksX; y1 := ObenLinksY; x2 := UntenRechtsX; y2 := UntenRechtsY; end;
procedure TRing.Zeichnen (myCanvas : TCanvas); begin myCanvas.Pen.Color := GetFarbe; myCanvas.Pen.Width := GetStaerke; myCanvas.Ellipse(x1,y1,x2,y2); end;
constructor TRing.Create; begin inherited Create; Init; end;
destructor TRing.Free; begin end;
procedure TRing.Init; begin SetStaerke(3); SetFarbe(clLime); SetPosition(50,50,150,150); end;
end. |
Beim Highlight tritt der Fehler auf, ich stehe auf dem Schlauch und weis nicht weiter. :nixweiss:
Gruß Dude566
Mitmischer 1703 - Mo 16.11.09 21:30
Item ist ein Array von 1..5, aber in der for-Schleife greifst du auf 0..4 zu - und wenn du auf 0 zugreifst, knallt's :)
Dude566 - Mo 16.11.09 22:14
Ja aber der Index geht doch bei 0 los. :nixweiss:
Und selbst wenn ich es korrigiere wie du sagst tritt der fehler immer noch auf.
Auch eine Zugriffsverletzung.
Niko S. - Di 17.11.09 07:09
Sicher dass du bei der for schleife jetzt 1 to 5 hast?
Außerdem musst du bei "Free" nicht auch zum schluss den vorherigen Code übernehmen "inhertied" ?
(Schlimm wenn einem die richtigen Worte fehlen)
BenBE - Di 17.11.09 12:00
1. Ein Destructor heißt Destroy
2. Free Überschreibt man nicht
3. Maximal Destroy überschreiben, dann aber nicht vergessen, inherited aufzurufen (WICHTIG!)
4. Deine Indizes hauen nicht hin (Deklaration 1..5, Nutzung in der Schleife 0..4)
5. Warum lagerst Du das Zeug in eine zusätzliche Methode Init aus?
6. Ggf. eine etwas regelmäßiger Code-Formatierung angewöhnen.
Dude566 - Di 17.11.09 15:06
BenBE hat folgendes geschrieben : |
1. Ein Destructor heißt Destroy
2. Free Überschreibt man nicht
3. Maximal Destroy überschreiben, dann aber nicht vergessen, inherited aufzurufen (WICHTIG!)
|
Unsere Lehrerin hat uns das gesagt, aber die hat meistens eh keine Ahnung warum sie das so und so macht. :roll:
Zum Rest: Ich probiere dann mal den Fehler zu beheben, werde mir auch
Luckies Tutorial mal reinziehen, und mich bei Fragen dann wieder hier melden.
Edit: Warum im Destructor das inherited aufrufen? Ich begfreife nicht so recht was das macht, unsere Lehrerin konnte es auch nicht erklären.
Nersgatt - Di 17.11.09 15:36
Du überschreibst ja den Destruktor von TObject, weil Du von TObject abgeleitet hast. Mit dem Aufruf von Inherited ruft Du dann den Destructor der Basisklasse (hier TObject) auf. So wird der Code, der im Destructor von TObject steht, auch ausgeführt. Ohne Inherited unterschlägst Du ihn. Das kann gefährliche Nebeneffekte haben.
Mitmischer 1703 - Di 17.11.09 16:08
Dude566 hat folgendes geschrieben : |
| Ja aber der Index geht doch bei 0 los. :nixweiss: |
Nein ;)
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| type TContainer = class(TObject) private Item : array [1..5] of TRing; public constructor Create; destructor Free; procedure Init;
end; |
Was genau hast du denn verändert :) ?
Dude566 - Di 17.11.09 16:47
Critter hat folgendes geschrieben : |
Hi,
[url]hier[/url] hatte ich kürzlich mal ein kleines Beispiel zu inherited gepostet, vielleicht hilft das beim Verständnis.
critter |
Ich glaube du hast bei der Angabe des Links einen Fehler gemacht. ;)
HelgeLange - Di 17.11.09 16:56
Du rufst auch ein inherited beim Create auf, obwohl du es nicht überschrieben hast und ich glaube, TObject bietet auch kein Create an.
Critter - Di 17.11.09 17:04
Hi,
Dude566 hat folgendes geschrieben : |
| Ich glaube du hast bei der Angabe des Links einen Fehler gemacht. ;) |
Ja das habe ich wohl :(. Habe den Link oben Repariert, aber zur Sicherheit gibt es den jetzt gleich noch einmal ;):
http://www.delphi-forum.de/viewtopic.php?p=580740#580740
critter
Dude566 - Di 17.11.09 21:02
Also ich habe in TContainer jetzt folgendes:
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:
| unit uTContainer;
interface
uses uTRing, Graphics;
type TContainer = class(TObject) private Item : array [1..5] of TRing; public constructor Create; destructor Destroy; end;
implementation
constructor TContainer.Create; var i : integer; begin for i := 1 to 5 do Item[i] := TRing.Create; end;
destructor TContainer.Destroy; var i : integer; begin inherited Create; for i := 1 to 5 do Item[i].Destroy; end;
end. |
Und bekomme immer noch die Zugriffsverletzung, ich dachte ich hätte es jetzt so wie ihr sagt angepasst. :nixweiss:
Xentar - Di 17.11.09 21:26
1. Verschieb das inherited create; auch ins Create.. im Destroy hat das nix zu suchen.
2. Nicht Item[i].Destroy aufrufen, sondern .Free
ersteres sollte dir bei deinem Problem weiterhelfen.
Dude566 - Di 17.11.09 21:29
Xentar hat folgendes geschrieben : |
1. Verschieb das inherited create; auch ins Create.. im Destroy hat das nix zu suchen.
2. Nicht Item[i].Destroy aufrufen, sondern .Free
ersteres sollte dir bei deinem Problem weiterhelfen. |
Nein hat es leider nicht, und ich habe Free in Destroy auf anraten einer deiner Vorredner geändert.
HelgeLange - Di 17.11.09 21:48
Man ruft kein destroy auf, nimm free
ausserdem lösch das "inherited Create" complett raus, TObject hat kein create zum überschreiben und im Destroy hat das schon garnichts zu suchen.
Xentar - Di 17.11.09 21:54
Dude566 hat folgendes geschrieben : |
| Nein hat es leider nicht |
Maa, dann schmeiß halt den Debugger an ;)
Setz nen Haltepunkt, geh Schritt für Schritt durch, und guck, wo der sich aufhängt.. vielleicht hast auch noch nen Fehler in der Ring Klasse, oder so?
jaenicke - Mi 18.11.09 11:11
Xentar hat folgendes geschrieben : |
| vielleicht hast auch noch nen Fehler in der Ring Klasse, oder so? |
Zumindest in der geposteten Version ist das auch so, wie sofort zu sehen ist:
Dude566 hat folgendes geschrieben : |
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| destructor Free; ...
destructor TRing.Free; begin end; | |
Was passiert jetzt? Einerseits ist Free jetzt nicht mehr das originale Free, andererseits wird zusätzlich auch noch der geerbte Destruktor nicht mehr aufgerufen.
Kurz gesagt: alles kaputt was kaputtzumachen geht. :mrgreen:
Also:
destructor Destroy; und dort
inherited; nicht vergessen, fertig...
Martok - Mi 18.11.09 11:12
Xentar hat folgendes geschrieben : |
| Maa, dann schmeiß halt den Debugger an ;) |
Tut mir leid jetzt hier quasi-offtopic dazwischenzuhauen.
Debugging lernt man in der Schule nicht. Zumindest kenne ich keinen wo das gemacht worden wäre. Warum das so ist, keine Ahnung. Entspricht aber voll und ganz dem im deutschen Schulsystem verankerten "auswendiglernen statt verstehen". Selber debuggen würde ja eine Fähigkeit schulen, wo kommen wir denn da hin. Da lernt man lieber Code auswendig und schreibt den dann in irgendwelchen Tests hin.
Wäre es anders, würden diesem Forum locker 50% der Fragen "erspart" bleiben.
Dude566 - Mi 18.11.09 18:21
Könnt ihr euch einigen?
HelgeLange und
jaenicke sagen beide was anderes. Ich probiere noch mal rum.
Aber wie
Martok schon sagte, lernt man nie Debugging, die Lehrer selbst könnens ja meistens nicht mal.
Edit: Habe mir jetzt mal schnell das Tutorial von meinem Vorposter angeschaut und bin den Text mal mit F7 Zeilenweise durchgegangen.
An einer Stelle komme ich dann nicht weiter und es kommt die Zugriffsverletzung:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| constructor TContainer.Create; var i : integer; begin for i := 1 to 5 do Item[i] := TRing.Create; end; |
Dude566 - Mi 18.11.09 18:50
Ja das ist traurig aber wirklich wahr, deshalb finden wir oft Fehler erst spät oder garnicht. :roll: :autsch:
Hier der komplette Code:
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:
| unit uTContainer;
interface
uses uTRing, Graphics;
type TContainer = class(TObject) private Item : array [1..5] of TRing; public constructor Create; destructor Destroy; end;
implementation
constructor TContainer.Create; var i : integer; begin for i := 1 to 5 do Item[i] := TRing.Create; end;
destructor TContainer.Destroy; var i : integer; begin for i := 1 to 5 do Item[i].Destroy; end;
end. |
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:
| unit uTRing;
interface
uses Graphics;
type TRing = class(TObject) private stift : integer; farbe : TColor; x1,y1,x2,y2 : integer; public constructor Create; destructor Destroy; procedure Init; procedure SetStaerke(dicke : integer); function GetStaerke : integer; procedure SetFarbe (stiftfarbe : TColor); function GetFarbe : TColor; procedure SetPosition(ObenLinksX,ObenLinksY,UntenRechtsX,UntenRechtsY : integer); procedure Zeichnen (myCanvas : TCanvas); end;
implementation
procedure TRing.SetStaerke(dicke : integer); begin stift := dicke; end;
function TRing.GetStaerke : integer; begin result := stift; end;
procedure TRing.SetFarbe (stiftfarbe : TColor); begin farbe := stiftfarbe; end;
function TRing.GetFarbe : TColor; begin result := stift; end;
procedure TRing.SetPosition(ObenLinksX,ObenLinksY,UntenRechtsX,UntenRechtsY : integer); begin x1 := ObenLinksX; y1 := ObenLinksY; x2 := UntenRechtsX; y2 := UntenRechtsY; end;
procedure TRing.Zeichnen (myCanvas : TCanvas); begin myCanvas.Pen.Color := GetFarbe; myCanvas.Pen.Width := GetStaerke; myCanvas.Ellipse(x1,y1,x2,y2); end;
constructor TRing.Create; begin Init; end;
destructor TRing.Destroy; begin inherited; end;
procedure TRing.Init; begin SetStaerke(3); SetFarbe(clLime); SetPosition(50,50,150,150); end;
end. |
Xentar - Mi 18.11.09 20:31
Tut bei mir, abgesehen von zwei Compilerwarnungen, einwandfrei.
Wie erzeugst du das ganze?
In welcher Zeile bleibt Delpi stehen, wenn der Fehler auftritt?
BenBE - Mi 18.11.09 23:03
Zur Erklärung ggf. mal folgende (konventionellen) Regeln:
1. In einem Construktor sollte inherited Create; die erste Zeile nach dem Begin sein. Dies ändert sich nur, wenn für diesen Aufruf lokale Variablen oder andere Dinge vorzubereiten sind. Bis zu diesem Zeitpunkt sind Zugriffe auf Instanz-Variablen aber absolutes Tabu!
2. In einem Destructor mit Namen Destroy ist die Letzte Zeile vor dem End der Aufruf der Vorfahrklasse. Diese Konvention ist hier zwingend, da es danach keine weiteren Aufgaben mehr gibt.
3. (wurde aber schon gesagt): Der Aufruf von Destroy erfolgt indirekt über die Methode Free.
Anmerkungen:
- Zum Überschreiben des Konstruktors bei TObject benötigt man das Methoden-Flag reintroduce;. Siehe Hilfe für mehr Informationen dazu.
- Construcotr und Destructor erzeugen intern spezielle Aufruf-Konventionen, bei denen neben den eigentlichen Argumenten auch weitere Daten übergeben. Diese regeln u.a. ob die Methode NewInstance aufgerufen wird, die den Speicher alloziiert. Eine genaue Erklärung führt hier zu weit.
Dude566 - Do 19.11.09 00:22
Xentar hat folgendes geschrieben : |
Tut bei mir, abgesehen von zwei Compilerwarnungen, einwandfrei.
Wie erzeugst du das ganze?
In welcher Zeile bleibt Delpi stehen, wenn der Fehler auftritt? |
Delphi-Quelltext
1: 2:
| for i := 1 to 5 do Item[i] := TRing.Create; |
Beim Create bricht mein Programm dann ab...
jaenicke - Do 19.11.09 01:21
Ich rate einmal (da du den Quelltext davon nicht postest):
Du erzeugst dein TContainer so...
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| var Container: TContainer; begin Container.Create; ... Container := Container.Create; |
Also eben nicht das Create der Klasse sondern der (nicht existenten) Instanz der Klasse. Da fehlt also das T vermute ich.
Dude566 - Do 19.11.09 17:15
jaenicke hat folgendes geschrieben : |
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| var Container: TContainer; begin Container.Create; ... Container := Container.Create; | |
Voll ins Schwarze :oops: :oops: , ich habe in meiner main.pas die erste Variante die du gepostet hast geschrieben, also wurde garkeine Instanz des Objekts TContainer erstellt. :autsch:
Sowas blödes! :autsch:
Aber warum wurde mir dann nicht schon dort ein Fehler angezeigt, ich meine der Compiler müsste das doch erkennen, wenn ich dort einfach komplett falsch ein Create für das Objekt aufrufe. :?
Mitmischer 1703 - Do 19.11.09 19:16
Ist halt ein Laufzeitfehler :zwinker:
jaenicke - Fr 20.11.09 00:50
Dude566 hat folgendes geschrieben : |
| Aber warum wurde mir dann nicht schon dort ein Fehler angezeigt, ich meine der Compiler müsste das doch erkennen, wenn ich dort einfach komplett falsch ein Create für das Objekt aufrufe. :? |
Sofern es eine lokale Variable ist, sollte eine Warnung kommen, dass die Variable evtl. nicht initialisiert ist. Wenn es aber eine globale ist, hat der Compiler keinerlei Möglichkeit zu erkennen, ob da nicht irgendwoher schon ein Objekt drin ist. ;-)
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!