Registriert: Sa Jan 01, 2005 17:11 Beiträge: 2068
Programmiersprache: C++
Hi, habe bei der Umsetzung "meiner" Fensterklasse Probleme von Objekte auf Classen umzustellen.
Sobald Render aufgerufen wird stürzt das Programm ab, da der Pointer auf was falsches zeigt.
Wieso?
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Ohne mir den Code auch nur anzusehen. Pointer auf Klassen sind absolut schwachsinnig, da Klasseninstanzen bereits Pointer sind. Macht die Sache also nur unnötig kompliziert.
An deiner Stelle würde ich die Pointer auf die Klassen komplett durch Klasseninstanzen ersetzen und dann noch mal schauen ob es nicht vielleicht schon geht.
[edit] Ich habe es doch mal gewagt in den Code zu schauen. Du solltest dir mal etwas über OOP durchlesen. Die internen Variablen deiner Klassen sollten die Sichtbarkei protected oder private haben. Entsprechend sollten alle nach außen hin sichtbaren Methoden / Eigenschaften public sein. Im Konstructor und Destruktor deiner Klassen solltest du IMMERinherited Create; oder inherited; aufrufen. Inherited ist die Kurzform von inherited Create. Im Destruktor entsprechend inherited; oder inherited Destroy;. Sollte dein Konstruktor/Destruktor nicht Create/Destroy heißen musst du das entsprechend ändern.
Registriert: Sa Jan 01, 2005 17:11 Beiträge: 2068
Programmiersprache: C++
Lossy eX hat geschrieben:
An deiner Stelle würde ich die Pointer auf die Klassen komplett durch Klasseninstanzen ersetzen und dann noch mal schauen ob es nicht vielleicht schon geht.
Könntest du mir dazu ein Codebeispiel geben? Wie du schon festgestellt hast, bin ich noch nicht so gut in OOP. Danke.
Lossy eX hat geschrieben:
Die internen Variablen deiner Klassen sollten die Sichtbarkei protected oder private haben. Entsprechend sollten alle nach außen hin sichtbaren Methoden / Eigenschaften public sein.
Meines Wissens nach, sind nicht definierte automatisch public. Die Teile sollen jedenfalls public sein, würden aber später auch über proberty umgebaut.
Lossy eX hat geschrieben:
Im Konstructor und Destruktor deiner Klassen solltest du IMMERinherited Create; oder inherited; aufrufen.
Die Frage ist ob ich wirklich muss. Durch da inherited initialiesiert er mir die Variablen der Vorgängerklasse.
Z.B. initialisiert TWindow die Variablen aus seinen Vorgänger TWidow mit.
Die Eigenschaften von TObject benutze ich ja nicht, deshalb müssten sie nicht initialisiert werden.
Wenn ich mich hier grundliegend irre, wäre die Angabe eines Linkes zu einem guten Tutorial hilfreich.
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Zitat:
Könntest du mir dazu ein Codebeispiel geben? Wie du schon festgestellt hast, bin ich noch nicht so gut in OOP. Danke.
Code:
TAppWindow =Class
private
Windows :Arrayof TWindow;// Hier anstelle von PWindow das TWindow verwenden.
procedure TAppWindow.Render;
var
Idx:Integer;
begin
...
for Idx:=0toHigh(Windows)do
begin
Windows[Idx].Render;// Kein Dereferenzieren (^) mehr notwendig.
Und das halt überall wo du auf einen Pointer auf eine Klasse zugreifst. Mir ist da aber noch etwas aufgefallen. In der Unit befindet sich eine variable AppWindow vom Typ TAppWindow. Wo wird die erstellt? Wird sie das überhaupt? Wenn nicht solltest du das tun.
Windows[High(Windows)]:=@self;// Das @ wird nicht mehr benötigt.
end;
end;
Jemandem wie mir der ausschließlich Objekt orientiert arbeitet rollen sich dabei die Fußnägel hoch. Für deinen Fall solltest du das @ weglassen und es direkt zuweisen. Und wenn du Zeit hast, dann eine Methode AddWindow(aWindow: TWindow) in der Klasse TAppWindow implementieren die es Selber in die Liste einbindet.
Und da bin ich auch schon beim nächsten Punkt.
Zitat:
Die Frage ist ob ich wirklich muss. Durch da inherited initialiesiert er mir die Variablen der Vorgängerklasse.
Also du musst das in deisem Fall nicht. Aber Objektorientiert gesehen sollten die Objekte nichts von der Existenz der anderen Wissen. Also so etwas wie diese Grundlegenden Eigenschaften setzen sollten sie immer schön selber tun. Und dann brauchst du das inherited. Ich würde es aber dennoch immer empfehlen, da es so nicht vergessen werden kann. Es frisst keine Geschwindigkeiten und wenn du dann tatsächlich mal den Konstruktor in der Basisklasse überschreibst so wird er wenigstens gleich aufgerufen. Und du rätselst nicht warum der Scheiß nicht aufgerufen wird. (Habe ich auch schon gehabt).
Du solltest da generell ein wenig Abstrakter rangehen. Da ist ein Objekt das macht das und da ist eines das macht das und zusammen tun sie das. Wirst du mit der Zeit noch lernen.
Zitat:
Meines Wissens nach, sind nicht definierte automatisch public. Die Teile sollen jedenfalls public sein, würden aber später auch über proberty umgebaut.
Sie sind Published. Das ist ziemlich gleich wie public. Aber es gibt durchaus Eigenschaften die von außen nicht angreifbar sein sollten. Und wenn da jemand ausversehen dran rum stellt zerhaut es dir alles. Und ohne hast du keinen Schutz davor. Die Sachen wie Visible kann man durchaus direkt public machen. Da gibt es nur 2 Möglichkeiten und keine kann einen falschen Wert beinhälten. Das ist jedem seinem Geschmack überlassen. Ich mache grundsätzlich alles Protected und Private und reiche alles andere mittels Properties nach außen.
Registriert: Sa Jan 01, 2005 17:11 Beiträge: 2068
Programmiersprache: C++
Danke für die genaue Ausarbeitung.
Das mit dem TWindow habe ich mir heute nachmittag zwar auch überlegt, war mir aber nicht sicher ob es so funktioniert oder ob ich so eine neue Instanze erstelle.
Aber wohl doch nicht .
Die Variable AppWindow wird im unteren Teil des Quellcodes vor der Initialisierung der Fenster gemacht.
Die Klasse ist im Prinizip da Einstellungen wie Fenstergrösse zu speichern und um alle Fenster zu rendern ohne sie einzeln (manuell) aufzurufen.
Der Grund weshalb sie schon als Var def. ist, ist der, dass mein Fenster sich bisher registrieren sollte.
Besser ist es wie du schon gesagt hast eine Methode zu erstellen, die die Fenster dann selbst einbindet.
Lossy eX hat geschrieben:
Du solltest da generell ein wenig Abstrakter rangehen. Da ist ein Objekt das macht das und da ist eines das macht das und zusammen tun sie das. Wirst du mit der Zeit noch lernen.
Wenn ich mir aktuell das überlege erscheint es mir logisch. Aktuell benötigt ein Knopf ein Fenster, damit es gezeichnet wird. Ansich sollte es aber auch so sein, dass ein Knopf alleine existieren sollte. Frag mich irgendwann später mal ob ich es geschafft habe.
Lossy eX hat geschrieben:
Die Sachen wie Visible kann man durchaus direkt public machen. Da gibt es nur 2 Möglichkeiten und keine kann einen falschen Wert beinhälten. Das ist jedem seinem Geschmack überlassen. Ich mache grundsätzlich alles Protected und Private und reiche alles andere mittels Properties nach außen.
Aktuell sind es nur Werte die auch public sein sollen, später wird es auch aufgegliedert.
Habe halt öfters versucht von Objecten (wo sowas nicht möglich ist) auf Klassen umzusteigen und bin dabei immer auf dieses Problem gestossen. Somit habe ich nur erstmal "lauffähig" übersetzen wollen bevor ich es genauer umsetze.
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
i0n0s hat geschrieben:
Aktuell benötigt ein Knopf ein Fenster, damit es gezeichnet wird. Ansich sollte es aber auch so sein, dass ein Knopf alleine existieren sollte.
Das ist klar. Aber um die Überlegung ein wenig in Richtung abstrakt zu treiben. Dem Button ist es egal ob er einen Parent hat. Wenn dann ist es ihm auch egal, ob es sich dabei um ein echtes Fenster oder ein Panel handelt. Es sind beides fensterähnliche Elemente oder Klassen. Falls du verstehst worauf ich hinaus will. Ich habe so ähnliche Probleme auch hinter mir. Ich habe allerdings bei null angefangen.
Registriert: Do Sep 25, 2003 15:56 Beiträge: 7810 Wohnort: Sachsen - ERZ / C
Programmiersprache: Java (, Pascal)
Schätze bisl theoretische Lektüre zu OOP würde dir weiterhelfen. Ich hab mal nach "OOP Delphi Tutorial" gegoogelt und das gefunden http://www.delphi-tutorials.de/tutorialsdelphi.htm. Keine Ahnung ob es dir weiterhilft. Aber du solltest bevor du losprogrammeirst dir ncohn bisl theorie anlesen. Klingt zwar staubig, hilft aber durchaus. Also auch so sachen wie Polymorphie, Abstrackte Klassen/Methoden, Interfaces(Wobei ich davon in Delphi auch keine Ahnung hab ) und so könnten dafür sorgen, dass du dein Projekt schnell nochmal umkrempelst und neu machst, dann aber richtig.
ich kann dir nämlich eins sagen, in nem halben Jahr weist du viel mehr über OOP und wirst magenkrämpfe bei denem jetzigen Code griegen. Deshalb leiber erstmal warten und Infos aufsaugen.
_________________ Blog: kevin-fleischer.de und fbaingermany.com
Grundsätzlich solltest Du alle Eigenschaften und Methoden aus folgenden Gründen so gut verstecken wie nur irgend möglich.
1.: Du weißt nie, was irgendwer mit deinen Klassen anstellt(auch Du selber). Ein Beispiel wäre, du hast eine Objektinstanz, auf die Du immer wieder zugreifst. Wenn die aber jemand von außen verändert(z.B. Nil setzt) , dann fliegt deine Klasse auf die Nase.
2.: Du kannst deine Klasse eventuell noch ändern, ohne alle Nachfolger durchzuwühlen, ob Du da noch ändern mußt. Ein Beispiel wäre, eine Liste, die Du zuerst als Array konzipiert hast, hinterher aber feststellst, daß Du eine Objektliste dafür benötigst
3.: Alle Klassen in einer Unit nimmt Delphi als 'befreundet' an, d.h. sie können gegenseitig auf Protected Eigenschaften zugreifen. Das ist oft aber Verhängnisvoll, zumal wenn Du dann beide Klassen beerbst und die Nachfolger wieder in der selben Unit landen. Dann können sie nämlich wechselseitig auch auf die Protected Member der jeweils anderen Vorfahrenklasse zugreifen.
4.: je weniger eine Klasse nach außen rausreicht, umso übersichtlicher wird sie von außen
5.: Du wirst dadurch gezwungen, deine Klassenhierarchie ordentlich zu strukturieren und Struktur ruled beim Programmieren
6.: Dein Testaufwand verringert sich. Alles was rausschaut, kann auch von außen gesetzt werden und deine Klasse muss das abkönnen. -> Du musst alle möglichen Zustände aller Member kombinieren, um festzustellen, ob eine Kombination zum Absturz führt.
7.- unendlichstens: hierzu fällt mir im Moment nix ein aber neue Gründe lassen sich immer finden
p.s.:'außen' ist in jedem Fall auch eine eventuelle Nachfolgerklasse. Hier gilt zusätzlich nur das virtuell machen was unbedingt nötig ist.
p.p.s.: Sehr hilfreich ist ausserdem wenn man am Variablennamen den Typ der Variable erkennen kann, z.B.:iName für Integervariablen, bName für Boolean, sName für Strings und oName für Objektinstanzen. Bei Arrays kann man z.B. vorne noch ein a anfügen und bei Übergabevariablen ein _, damit man sie von lokalen Variablen unterscheiden kann.
_________________ Manchmal sehen Dinge, die wie Dinge aussehen wollen, mehr wie Dinge aus, als Dinge.
<Esmerelda Wetterwax>
Es kann vorkommen, dass die Nachkommen trotz Abkommen mit ihrem Einkommen nicht auskommen und umkommen.
p.p.s.: Sehr hilfreich ist ausserdem wenn man am Variablennamen den Typ der Variable erkennen kann, z.B.:iName für Integervariablen, bName für Boolean, sName für Strings und oName für Objektinstanzen. Bei Arrays kann man z.B. vorne noch ein a anfügen und bei Übergabevariablen ein _, damit man sie von lokalen Variablen unterscheiden kann.
Diese Art der Namenvergabe wurde mit .Net aber wieder abgeschafft. Das einzige in der Hinsicht ist das I am Anfang von Interface Namen.
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Ich wüsste nicht, dass das irgendwann mal angeschafft war.
Kleiner Scherz. Bei API's sieht man so etwas recht häufig. Also so Windows API etc. Ich persönlich habe so etwas aber noch nie gemacht. Anhand des Namens ist eigentlich fast immer klar was gemeint ist. Außer man kürzt die Namen bir zur unkenntlichkeit runter, dann braucht man natürlich so Typbezeichner. Aber das ist eine Sache die jeder so machen kann wie es ihm beliebt.
Zitat:
Alles was rausschaut, kann auch von außen gesetzt werden und deine Klasse muss das abkönnen. -> Du musst alle möglichen Zustände aller Member kombinieren, um festzustellen, ob eine Kombination zum Absturz führt.
Muss man nicht sowieso alles sinnvoll und gründlich testen? Aber dafür gibt es normal Properties bei denen man eine Methode bei Write angibt. Dann wird das Stück code aufgerufen. Man kann abprüfen ob der Wert gültig ist und erst dann setzt man ihn auf die Variable. So kann man schon ausschließen, dass überhaupt ungültige Werte gesetzt werden können. Nur noch ein zusätzlicher Schutz.
Ich wüsste nicht, dass das irgendwann mal angeschafft war.
Simonyi, nach welchem die Hungarian Notation benannt wurde arbeitete fuer Word etc. Und nach http://www.joelonsoftware.com/articles/Wrong.html hatte er nie die Idee dass man den variablen Typen vor den Variablen Namen schreibt sondern die Art. Also wenn es sich bei einer Integer Variable um einen Reihen Index handelt sollte sollte man rIndex schreiben, so dass man schoen im code sehen kann wenn man faelschlicher weise ein Kolumnen Index Variable zb kIndex mit rIndex verrechnet.
Zitat:
The dark side took over Hungarian Notation. Nobody seems to know why or how, but it appears that the documentation writers on the Windows team inadvertently invented what came to be known as Systems Hungarian
Und so wurde dann das "type" mal als variablen Typen interpretiert....
@Lars: ich hab nicht von Property namen, sondern von internen Variablen gesprochen, wie z.B.: 'iLoop' für eine integer und 'eLoop' für eine enum Laufvariable oder 'asItems' für ein Array vom Typ String oder '_oSender' für ein übergebenes Senderobjekt bei einem Notify Handler.
Das würde dann so aussehen:
Code:
Class TFooBar=Class(TObject)
Private
sFoo: String;
iBar: Integer;
Public
Property Foo:String Read sFoo Write sFoo;
Property Bar:Integer Read iBar Write iBar;
End;
in .Net(c#) dann so:
Code:
public class FooBar: Object
{
private String sFoo;
private Integer iBar;
public String Foo
{
get
{
return sFoo;
}
set
{
sFoo=value;
}
}
public Integer Bar
{
get
{
return iBar
}
set
{
iBar=value;
}
}
}
p.s.: Auch in .Net macht es Sinn, zu wissen, welchen Typ eine Variable hat, ohne nachschauen zu müssen
p.p.s.: nebenbei fetzt das Ganze auch für die Code Vervollständigung, weil mann da einfach i hinschreibt, [Strg+Space] drückt und vóila alle Integervariablen werden aufgelistet
_________________ Manchmal sehen Dinge, die wie Dinge aussehen wollen, mehr wie Dinge aus, als Dinge.
<Esmerelda Wetterwax>
Es kann vorkommen, dass die Nachkommen trotz Abkommen mit ihrem Einkommen nicht auskommen und umkommen.
Denke heutzutage bringt das nicht mehr soviel, weil man einfach mit dem Mauszeiger über die Variable gehen kann und dann den Type und Deklarationsort angezeigt bekommt.
Bei BASIC gibt's es oder gab es zumindest mal diese Regel umgekehrt. $ am Ende des Namens für strings, % für Integer usw...
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Sidorion hat geschrieben:
p.p.s.: nebenbei fetzt das Ganze auch für die Code Vervollständigung, weil mann da einfach i hinschreibt, [Strg+Space] drückt und vóila alle Integervariablen werden aufgelistet
Da finde ich das mitunter auch als Nachteil, weil ich dann zu erst wissen muss was für einen typ ich suche bevor ich den Namen weiß. Aber ich denke mal das ist auch reine Ansichtssache.
Mitglieder in diesem Forum: 0 Mitglieder und 10 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.