AW: Was nervt euch so, während der Programmierung oder so allgemein
Beim Entwickeln mit Zeigern fand ich Assertions immer recht hilfreich um zu sehen wo invalide Zeiger in die Datenstruktur kommen, auch wenn sie da noch nicht benutzt werden.
Wenn man dann noch darauf achtet, das die Zeiger immer einen definierten Zustand haben und man typisierte Zeiger benutzt, kann gar nicht mehr so viel schief gehen (ausgenommen der Frage: wo/wann geben ich den Speicher wieder frei). Rechnen mit Zeigern sollte man tatsächlich nur betreiben, wenn man gut ausgeschlafen ist. Am besten werden diese Berechnungen dann in Prozeduren/Methoden gekapselt und ordentlich typisiert. |
AW: Was nervt euch so, während der Programmierung oder so allgemein
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Zitat:
Delphi-Quellcode:
Benutzung:
type
TDoubleLinkedListEntry<T> = class private var FValue: T; FPrevious: TDoubleLinkedListEntry<T>; FNext: TDoubleLinkedListEntry<T>; procedure SetValue(const Value: T); procedure SetNext(const Value: TDoubleLinkedListEntry<T>); procedure SetPrevious(const Value: TDoubleLinkedListEntry<T>); public constructor Create(const AValue: T; const APrevious, ANext: TDoubleLinkedListEntry<T>); property Value: T read FValue write SetValue; property Previous: TDoubleLinkedListEntry<T> read FPrevious write SetPrevious; property Next: TDoubleLinkedListEntry<T> read FNext write SetNext; end; TDoubleLinkedList<T> = class private var FHead, FTail: TDoubleLinkedListEntry<T>; type TListEnumerator = class private var FFirst: Boolean; FCurrent: TDoubleLinkedListEntry<T>; public constructor Create(AList: TDoubleLinkedList<T>); property Current: TDoubleLinkedListEntry<T> read FCurrent; function MoveNext: Boolean; end; public destructor Destroy; override; procedure Append(const Value: T); procedure Delete(const Value: T); procedure Remove(const Value: TDoubleLinkedListEntry<T>); function GetEnumerator: TListEnumerator; property Head: TDoubleLinkedListEntry<T> read FHead; property Tail: TDoubleLinkedListEntry<T> read FTail; end; { TDoubleLinkedListEntry<T> } constructor TDoubleLinkedListEntry<T>.Create(const AValue: T; const APrevious, ANext: TDoubleLinkedListEntry<T>); begin FValue := AValue; FPrevious := APrevious; FNext := ANext; end; procedure TDoubleLinkedListEntry<T>.SetNext(const Value: TDoubleLinkedListEntry<T>); begin FNext := Value; end; procedure TDoubleLinkedListEntry<T>.SetPrevious(const Value: TDoubleLinkedListEntry<T>); begin FPrevious := Value; end; procedure TDoubleLinkedListEntry<T>.SetValue(const Value: T); begin FValue := Value; end; { TDoubleLinkedList<T> } destructor TDoubleLinkedList<T>.Destroy; var CurrentEntry: TDoubleLinkedListEntry<T>; begin if Assigned(FHead) then begin CurrentEntry := FHead; while Assigned(CurrentEntry.Next) do begin CurrentEntry := CurrentEntry.Next; CurrentEntry.Previous.Free; end; FTail.Free; end; inherited; end; function TDoubleLinkedList<T>.GetEnumerator: TListEnumerator; begin Result := TListEnumerator.Create(Self); end; procedure TDoubleLinkedList<T>.Append(const Value: T); begin if Assigned(FTail) then begin FTail.Next := TDoubleLinkedListEntry<T>.Create(Value, FTail, nil); FTail.Next.Previous := FTail; FTail := FTail.Next; end else begin FTail := TDoubleLinkedListEntry<T>.Create(Value, FTail, nil); FHead := FTail; end; end; procedure TDoubleLinkedList<T>.Remove(const Value: TDoubleLinkedListEntry<T>); begin if Assigned(Value.Previous) then Value.Previous.Next := Value.Next; if Assigned(Value.Next) then Value.Next.Previous := Value.Previous; end; procedure TDoubleLinkedList<T>.Delete(const Value: T); var CurrentEntry: TDoubleLinkedListEntry<T>; begin CurrentEntry := FHead; while Assigned(CurrentEntry) do if TComparer<T>.Default.Compare(CurrentEntry.Value, Value) = 0 then begin Remove(CurrentEntry); CurrentEntry.Free; Break; end else CurrentEntry := CurrentEntry.Next; end; { TDoubleLinkedList<T>.TListEnumerator } constructor TDoubleLinkedList<T>.TListEnumerator.Create(AList: TDoubleLinkedList<T>); begin FFirst := True; FCurrent := AList.Head; end; function TDoubleLinkedList<T>.TListEnumerator.MoveNext: Boolean; begin Result := Assigned(FCurrent) and (Assigned(FCurrent.Next) or FFirst); if Result and not FFirst then FCurrent := FCurrent.Next; FFirst := False; end;
Delphi-Quellcode:
Und debuggen kann ich das auch problemlos:
var
Test: TDoubleLinkedList<Integer>; CurrentEntry: TDoubleLinkedListEntry<Integer>; begin Test := TDoubleLinkedList<Integer>.Create; try Test.Append(100); Test.Append(300); Test.Append(200); for CurrentEntry in Test do ShowMessage(IntToStr(CurrentEntry.Value)); // 100, 300, 200 Test.Delete(300); for CurrentEntry in Test do ShowMessage(IntToStr(CurrentEntry.Value)); // 100, 200 finally Test.Free; end; end; Anhang 37262 Sicher, explizite Zeiger braucht man an manchen Stellen natürlich. Solange man das ganze durchdacht strukturiert machen die aber auch nicht mehr Probleme als andere Sprachbestandteile. Ein Beispiel, das ich beruflich vor einer Weile brauchte, war ein generischer Ringpuffer. Da dort auch z.B. Records hineingespeichert werden können sollten, brauchte ich einen Pointer auf die gespeicherten Daten um diese verändern zu können. Dies wird leider nicht komplett unterstützt, aber vom Prinzip her sah das am Ende so aus:
Delphi-Quellcode:
Ohne Generics und den Pointer in Kombination wäre das gar nicht möglich das typsicher so umzusetzen. Aber trotzdem ist das ganze immer noch typsicher.
TCircularBuffer<T> = class
public type TBufferPointer = ^T; strict private var FElements: TArray<T>; // ... public type TBufferEnumerator = class(TEnumerator<TCircularBuffer<T>.TBufferPointer>) // ... public constructor Create(ABuffer: TCircularBuffer<T>); property Current: TCircularBuffer<T>.TBufferPointer read GetCurrent; function MoveNext: Boolean; end; function GetEnumerator: TBufferEnumerator; reintroduce; // ... end; |
AW: Was nervt euch so, während der Programmierung oder so allgemein
Ich wünschte ich hätte Generics :(
Edit: Btw ich verstehe nicht, wieso immer alle auf Pointern rumhacken. Wenn der Code mit Pointern schwer zu debuggen ist, wäre er mit Objekten oder Referenzen (wenn Delphi sowas hätte) genau so schwer zu debuggen. Es liegt an der Struktur des Codes, nicht an den Pointern. Ich verwende manchmal Pointer, vor allem wenn ich mit irgendwelchen Buffern hantiere, und habe damit eigentlich nie Probleme. |
AW: Was nervt euch so, während der Programmierung oder so allgemein
Ich denke da gerade an Beispiel aus C, bei denen es Pointer auf Pointer gab, die dann *huppsala* wieder auf Pointer zeigten. Das hat schon ein gewisses Potential für einen Knoten im Hirn. Einen einfachen Pointer auf eine Datenstruktur jedoch sehe ich nun auch nicht als "per se" problematisch an - bei vielen API-Funktionen von Windows ist es die einzige Art, diese zu verwenden.
|
AW: Was nervt euch so, während der Programmierung oder so allgemein
In Delphi ist das intern ja auch nicht anders, bleibt dem Entwickler aber zum größten Teil verborgen (zum Glück).
|
AW: Was nervt euch so, während der Programmierung oder so allgemein
Delphi referenziert/dereferenziert auch automatisch.
|
AW: Was nervt euch so, während der Programmierung oder so allgemein
Na, da habe ich wohl wieder etwas losgetreten. Also das gute vorweg: Ich nahm mich der Sache noch einmal an, und jetzt funktioniert es. Es ist die Langzahlenarithmetik aus dem "Arbeitsbuch für Turbo Pascal 4.0" aus dem ehrenhaften Jahre 1988. Delphi mit seiner beeindruckenden Abwärtskompatibilität hat an "nur-Turbo-Pascal-tauglichen" Quelltexten als solchen natürlich überhaupt nichts auszusetzen. Ein Fehler war m.E. schon im Buch, bei einem anderen bezweifele ich, daß er auch in Turbo-Pascal schon auftrat. Wenn ein Variablenparameter in einer Prozedur einen anderen Wert hat (auch unmittelbar vor ihrem Verlassen) als danach, dann sieht man ganz schon alt aus, weil sich diese Wertänderung beim Debuggen nicht nachverfolgen läßt. Der andere Wert ist aus heiterem Himmel eben plötzlich da. Da ich so etwas vorher noch nie hatte, blieb mir nichts anderes übrig, als "die bösen Zeiger" als Schuldige auszumachen, weil (für mich) kaum nachvollziebar ist, wann nur Zeiger (=Adressen) und wann Speicherinhalte geändert werden. Vielleicht lag in einem dieser Nebeneffekte ja die Ursache.
Zitat:
|
AW: Was nervt euch so, während der Programmierung oder so allgemein
Ach so, du machst das mit Records. Das macht es unnötig kompliziert. Mit Klassen wie in meinem Beispiel geht es einfacher. Das sind implizite Pointer, weil das einfach eine Variable mit einem Objekt drin ist. Ein expliziter Pointer ist ein Pointer auf einen Record oder so, den man auch als solchen behandeln muss.
Wichtig ist natürlich auch immer, dass man die einzelnen Funktionen möglichst kurz und übersichtlich macht und nicht zu viel in eine. Das lässt sich sonst gerade bei etwas komplexerer Logik (gut ist ne doppelt verkettete Liste nicht wirklich, aber zumindest ein bisschen) schwer sehen, ob das so richtig ist. Jedenfalls hilft da FastMM beim Debuggen, weil man da Stacktraces bekommt wo das Objekt erzeugt und wo freigegeben wurde, wenn man danach noch einmal darauf zugreift. |
AW: Was nervt euch so, während der Programmierung oder so allgemein
So allgemein nervt mich, dass bei etlichen Verpackungen das Mindesthaltbarkeitsdatum völlig woanders steht als „Mindestens haltbar bis:“. Es ist eine Kleinigkeit, aber einfach unnötig. Wenn es eh darauf hinausläuft, dass ich die ganze Verpackung nach dem erstbesten Datum absuchen muss und einfach davon ausgehen muss, dass es sich dabei um das Mindesthaltbarkeitsdatum handelt, könnten sie sich den Text auch gleich sparen.
|
AW: Was nervt euch so, während der Programmierung oder so allgemein
Zitat:
Zitat:
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:04 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz