Dennis S. schrieb:> Bin kein C#-Programmierer, aber die Antwort findest du hier von "Kevin> Pang":
Das ist die perfekte Diskussion mit schlüssiger Erklärung!
Peter II schrieb:> Wenn man nicht alle Exception abfängt oder eine neue Exception wirft.
Jetzt habe ich's :-)
Thanks 2gether!
Oder falls Console.WriteLine() eine Exception wirft.
Das if (file != null) ? Normalerweise macht man das Open im Try, damit
das Catch auch die Open-Exceptions bearbeitet. Falls die Exception vor
dem Open auftritt, schmeißt das Close eine unsinnige Exception.
finally ist ein Hilfskonstrukt, den man braucht, weil Objekte in C# ihre
Ressourcen nicht beim Verlassen der Funktion automatisch freigeben.
Das ist der Nachteil, den man sich durch einen Garbage Collector
einhandelt.
Rolf M. schrieb:> den man braucht, weil Objekte in C# ihre> Ressourcen nicht beim Verlassen der Funktion automatisch freigeben.
Was ist das für ein Unfug?
Es ist eher eine elegante Methode garantiert Ressourcen freizugeben, die
eben NICHT automatisch freigegeben werden, auch im Fehlerfall.
Oder wird in deiner Programmiersprache eine Datenbankverbindung, ein
offenes File oder HTTP oder TCP Verbindung automatisch freigegeben wenn
du die Funktion/Methode verlässt?
Klar, genau dann wenn du den Rechner ausschaltest :-)
Der Andere schrieb:> Oder wird in deiner Programmiersprache eine Datenbankverbindung, ein> offenes File oder HTTP oder TCP Verbindung automatisch freigegeben wenn> du die Funktion/Methode verlässt?
klar doch. In C++ wird jedes Destuktur bei verlassen vom
Gültigkeitsbereich aufgerufen.
Damit hat man ein definiertes verhalten, was es (leider) bei C# so nicht
gibt.
Peter II schrieb:> Der Andere schrieb:>> Oder wird in deiner Programmiersprache eine Datenbankverbindung, ein>> offenes File oder HTTP oder TCP Verbindung automatisch freigegeben wenn>> du die Funktion/Methode verlässt?>> klar doch. In C++ wird jedes Destuktur bei verlassen vom> Gültigkeitsbereich aufgerufen.
Wenn das Objekt auf dem Stack liegt, ja. Und wieviele Objekte liegen auf
dem Heap?
Mark B. schrieb:> Wenn das Objekt auf dem Stack liegt, ja. Und wieviele Objekte liegen auf> dem Heap?
Eben, gerade teure Objekte wie DB Verbindungen, TCP verbindungen, etc.
oft genug nicht.
Mark B. schrieb:> Wenn das Objekt auf dem Stack liegt, ja. Und wieviele Objekte liegen auf> dem Heap?
In modernem C++ so gut wie keine. Das spielt aber keine Rolle, solange
dass Objekt zur Verwaltung der Datenbank-Verbindung auf dem Stack liegt.
Und in C++ kann man sogar unterbinden, dass solche Objekte auf dem heap
alloziiert werden.
Torsten R. schrieb:> In modernem C++ so gut wie keine. Das spielt aber keine Rolle, solange> dass Objekt zur Verwaltung der Datenbank-Verbindung auf dem Stack liegt.> Und in C++ kann man sogar unterbinden, dass solche Objekte auf dem heap> alloziiert werden.
Bei der Masse an real existierenden Speicherlecks scheint es dann wohl
nicht sehr viel modernen C++ Code zu geben. ;-)
Der Andere schrieb:> Oder wird in deiner Programmiersprache eine Datenbankverbindung, ein> offenes File oder HTTP oder TCP Verbindung automatisch freigegeben wenn> du die Funktion/Methode verlässt?
Na ja, das ist der Grund für Destruktoren und Klassen.
In klassischem C
1
intfunc(intparam)
2
{
3
char*buffer=malloc(1024);
4
if(!buffer)return0;
5
if(!param)// spät entdeckter Fehler
6
{
7
free(buffer);// ja, hier kann/muss man den Fall extra behandeln
8
return0;// Rückkehr
9
}
10
use(param,buffer);
11
free(buffer);
12
return1;
13
}
Wenn man eine Programmiersprache mit Exceptions hat, funktioniert kein:
1
intfunc(intparam)
2
{
3
char*buffer=malloc(1024);
4
if(!buffer)return0;
5
if(!param)throw(whatever);// bzw. longjmp(outofhere); in C
6
use(param,buffer);
7
free(buffer);// oops, not freed after throw/logjmp
8
return1;
9
}
man braucht Klassen mit Destruktoren, damit der Code gut wird
Mark B. schrieb:> Bei der Masse an real existierenden Speicherlecks scheint es dann wohl> nicht sehr viel modernen C++ Code zu geben. ;-)
Die Speicherlecks kommen eher aus diesem C-Mit-Klassen Style (wie das
Beispiel von Michael Bertrandt).
In zwei Projekten, in denen ich den direkten Vergleich hatte, war der
Server in C++ geschrieben, der Client in einem Fall in Java und im
anderen Fall in C#. In beiden Fällen, gab es überhaupt kein Problem mit
Resource-Leakes auf dem Server und die Software lief Wochen und Monate
ohne Problem durch. Die Clients hatten häufig Probleme mit irgend wo
vergrabenen Referenzen, die noch erreichbar waren, aber nicht mehr
erreichbar sein sollten.
Michael Bertrandt schrieb:
> Exceptions und Klassen hängen also konzeptionell direkt zusammen.
Nö, ich benutze im Embedded Bereich Klassen ohne Exceptions. Und
Exceptions habe ich schon unter VMS implementiert gesehen und die OS-API
hatte garantiert keine Klassen.
Der Andere schrieb:> Rolf M. schrieb:>> den man braucht, weil Objekte in C# ihre>> Ressourcen nicht beim Verlassen der Funktion automatisch freigeben.>> Was ist das für ein Unfug?> Es ist eher eine elegante Methode garantiert Ressourcen freizugeben, die> eben NICHT automatisch freigegeben werden, auch im Fehlerfall.
Richtig. Und in C++ braucht man diese "elegante Methode" nicht. Man hat
stattdessen Destruktoren, die das auf einem objektorientierten Weg
umsetzen.
> Oder wird in deiner Programmiersprache eine Datenbankverbindung, ein> offenes File oder HTTP oder TCP Verbindung automatisch freigegeben wenn> du die Funktion/Methode verlässt?
Ja. Einfaches Pseudocode-Beispiel:
1
voidreadFile(stringfilename)
2
{
3
Filefile;
4
file.open(filename);
5
Bufferbuffer=file.readAll();
6
Parserparser;
7
data_=parser.parse(buffer);
8
}
Zugegeben etwas konstruiert, aber man kann sich vorstellen, worauf das
rauslaufen soll. Mehrere Aktionen werden durchgeführt, und dafür werden
temporär Ressourcen benötigt, die nachher wieder freizugeben sind.
Wenn die Funktion ihr Ende erreicht oder irgendwo zwischendrin eine
Exception ausgelöst wird, wird in C++ alles wieder freigegeben. Der
File-Destruktor kümmert sich dabei darum, nicht nur den Speicher, den
das File-Objekt selbst für das File-Handle braucht, freizugeben, sondern
auch die Datei zu schließen. Man muss nicht wissen, daß das Objekt
intern noch andere Ressourcen als Speicher braucht und sich auch nicht
explizit darum kümmern, diese von Hand freizugeben. So wird das Prinzip
der Kapselung besser umgesetzt als in Java oder C#.
Ich kenne das Problem z.B. von Lotus Notes, bei dem beim Laden oder
Speichern von Mail-Anhängen auf einem USB-Stick dieser nicht sauber
ausgeworfen werden kann, bis man Notes beendet, da offenbar irgendwo
vergessen wurde, die Datei wieder explizit zu schließen und diese
deshalb für immer (bzw. bis zum Programm-Ende) offen bleibt.
Das ist für mich das Problem mit Garbage Collecoren. Sie kümmern sich
nicht ganzheitlich um ein vernünfiges Ressourcenmanagement, sondern nur
um den Speicher. Alle anderen Ressourcen werden nicht automatisch
freigegeben, und man hat auch keine Möglichkeit, es vernünftig zu
automatisieren. Und genau deshalb gibt es "finally".
Mark B. schrieb:>> In C++ wird jedes Destuktur bei verlassen vom Gültigkeitsbereich>> aufgerufen.>> Wenn das Objekt auf dem Stack liegt, ja. Und wieviele Objekte liegen auf> dem Heap?
Wenn man's richtig macht, nur wenige bis gar keine, um die man sich
explizit kümmern muss. Dafür gibt's in C++ Container und Smart Pointer.
Mark B. schrieb:> Torsten R. schrieb:>> In modernem C++ so gut wie keine. Das spielt aber keine Rolle, solange>> dass Objekt zur Verwaltung der Datenbank-Verbindung auf dem Stack liegt.>> Und in C++ kann man sogar unterbinden, dass solche Objekte auf dem heap>> alloziiert werden.>> Bei der Masse an real existierenden Speicherlecks scheint es dann wohl> nicht sehr viel modernen C++ Code zu geben. ;-)
Eher gibt es mehr sogenannte C++ Programmierer, die mit dem Begriff
'RAII' nichts anfangen können. RAII führt recht problemlos zu
Programmen, die keine Speicherlecks aufweisen. Nur muss man es
konsequent umsetzen. Und daran hapert es halt meistens (und ich nehme
mich da durchaus auch nicht davon aus - an der Konsequenz).
In C# gibt es auch noch "using", damit ist zumindest das explizite
"finally" bzw. der Aufruf von Dispose/Close (*) unnötig. Man sollte
natürlich in Klassen, die entsprechende Ressourcen auf Klassenebene
besitzen, IDisposable implementieren.
(*) Was meistens, aber nicht immer äquivalent ist.
Michael B. schrieb:> Wenn man eine Programmiersprache mit Exceptions hat, funktioniert> kein:> int func(int param)> {> char *buffer=malloc(1024);> if(!buffer) return 0;> if(!param) throw(whatever); // bzw. longjmp(outofhere); in C> use(param,buffer);> free(buffer); // oops, not freed after throw/logjmp> return 1;> }
Na ja, so ungefähr:
1
voidfunc(intparam)
2
{
3
autobuffer=std::make_unique<char[]>(1024);
4
if(!param)throw(whatever);
5
use(param,buffer.get());
6
}
Was nicht bedeutet, dass man es so machen muss oder sollte.