Diskussion:Ressourcenbelegung ist Initialisierung

message: Ich glaube, ein kleines Code-Beispiel wäre nicht schlecht. --jpp ?! 23:42, 6. Jun 2006 (CEST)
thread_title:  Verständlichkeit 

message: Das Beispiel ist nicht wirklich gut. Erstens wird es bereits durch std::ofstream implementiert, zweitens wird die Klasseninvariante "Datei ist während der gesamten Lebenszeit der Klasse geöffnet" nicht implementiert, drittens wird die Regel der großen Drei nicht beachtet. --Phst 10:10, 16. Aug. 2010 (CEST)
replies: text: Berechtigter Einwand. Aber welches Beispiel wäre denn für RAII deiner Meinung nach besser geeignet? --RokerHRO 21:32, 4. Feb. 2012 (CET)

text: Gerade bei nicht kopierbaren Objekten wie Dateihandles ist die Dreierregel nicht sinnvoll anzuwenden, da der Destruktor einer Kopie (shallow oder deep, das ist egal) auch die Quelle zerstört (Datei schließt). Objekte dürfen nur kopier- oder zuweisbar sein, wenn deren interne Resoucen auch vervielfältigbar sind (siehe Mißbrauch von auto_ptr). Das Beispiel ist aber aus anderen Gründen nur halbgut.--46.115.107.186 17:58, 31. Jan. 2013 (CET)
thread_title:  Beispiel 

message: Der Artikel beschäftigt sich überwiegend mit der Ressourcenfreigabe und nicht mit der zu fordernden Initialisierung.
Nach meinem seit 20 Jahren praktizierten Verständnis von RAII ist die automatische Freigabe nur ein unbedeutender Nebeneffekt des Konstruktor/Destruktor-Konzepts. Das Konzept ist auch als "Declaration is Initialization" bekannt.
*1. Ressourcenbelegung/Deklaration erst, wenn das Objekt auch gebraucht wird und alle Informationen zur Initialisierung auch bekannt sind (zB Dateiname oder Bildgröße)
*2. Keine Reservierung/Deklaration leerer Objekte auf Vorrat (zB im Dekalrationsblock am Anfang einer Funktion nach C-, Basic-, Pascal- oder Fortran-Konvention, auch Java), es sei denn, daß auch ein leeres Objekt sinnvoll und fehlerfrei benutzt werden kann (zB ein leerer String oder Integer, aber eine nicht initialisierte Fleißkommazahl kann auch ungültig (NaN) sein) und es gute Gründe dafür gibt
*3. Möglichst kleiner Gültigkeitbereich (Scope) eines Objekts, es sei denn, es wäre wohlüberlegt (zB Pufferobjekt für eine Schleife einmalig außerhalb anlegen)
*4.a die Existenz und Erreichbarkeit eines Objekts impliziert seine Gültigkeit
*4.b nach der Initialisierung muß jeder bestimmungsgemäße Zugriff oder Aufruf mit zulässigen Parametern auch ein gültiges Ergebnis liefern und die Konsistenz des Objekts erhalten (vergleiche analog BIBO-Stabilität, "Valid-In-Valid-Out")
*4.c Gültigkeitsüberprüfungen innerhalb des erfolgreich angelegten Objekts sind sinnlos ("Bin ich gültig? Lebe ich?", die Antwort "Nein" ist paradox oder wenigstens inkonsistent, Gödelscher Unvollständigkeitssatz, "Alle Kretaer sind Lügner ... sagt der Kretaer"), außer beim Destruktor, damit ein (teilweise) ungültig initialisiertes Objekt die bereits erfolgreich belegten Ressourcen wieder freigeben kann
*4.d während der Debug-Phase dürfen/müssen die übergebenen Parameter sehr wohl überprüft werden; ebenso darf/muß geprüft werden, ob formal gültige Eingaben das Objekt ungültig, unbestimmt oder widersprüchlich machen (zB Zählerüberlauf, Kreisradius unendlich, Dreieck ohne Seiten, dessen Innenwinkelsumme nicht bestimmt werden kann)
*5. Standardkonstruktoren nur, wenn ein Mißbrauch ausgeschlossen ist (zB Zugriffsfunktion auf eine nicht existierende Datei); sonst Standardkonstruktor "verstecken"; gleiches gilt für den Copy-Konstruktor und den Standardzuweisungsoperator (shallow copy)
*6. Ein Objekt muß so konstruiert sein, daß die belegten Ressourcen während seiner Erreichbarkeit nicht ungültig werden können (zB weil es Seiteneffekte gibt, ein anderes Objekt die Kapselung umgehen kann, im ärgerlichsten Fall, daß eine Kopie des Objektes sich selbst löscht und dabei die Ressourcen der Quelle löscht)
Das ist meine Quintessenz aus RAII, die ich aus den Werken mehrerer Autoren so zusammengeführt habe. Wichtigste Quelle ist natürlich "The C++ Programming Language" von Bjarne Stroustrup, insbesondere für die ersten drei Regeln. Regel vier entspringt dem allgemeinen Grundsatz, daß der Benutzer eines Objekts für dessen sinnvollen Gebrauch selbst verantwortlich ist und daß der Objektentwickler nur ein konsistentes Objekt zu liefern hat. Regel 5 und 6 berücksicht neuere Spitzfindigkeiten, wie sie sich um die Intelligenten Zeiger (auto_ptr, unique_ptr) entwickeln, mit der Forderung: "Diese Schweinerei muß eine andere werden!" Der WebLink auf auto_ptr bezieht sich daher auf eine veraltete, oder wenigstens kritisierte Technik aus den Anfängen des RAII.
Ich ändere zunächst einmal den Beispiel-Code.--46.115.107.186 17:58, 31. Jan. 2013 (CET)
replies: text: Übrigens: die Delphi Variante von Pascal kann seit V10.3.3 auch inline variablen, man muss diese also nicht mehr zwingend im Deklarationsblock der Methode/Prozedur/Funktion deklarieren. --2A02:8070:6394:7A00:40FF:22CA:CF2F:5CBE 22:06, 10. Mär. 2022 (CET)
thread_title:  Thema verfehlt 

message: Das Beispiel ist nicht nur schlecht (C-Funktionen, die dank der C++-Standardbibliothek ohnehin nicht gebraucht werden), sondern auch fehlerhaft. Wird eine Instanz vom Typen Datei kopiert (entweder mit dem impliziten Kopierkonstruktor oder mit dem Zuweisungsoperator), so wird lediglich das Handle kopiert, womit sich beide Instanz auf ein und dieselbe Datei beziehen. Da kein Referenzzähler implementiert ist, kann eine Kopie die originale Instanz ungültig machen. Beispiel:
Datei Datei1("file.txt");
Datei(Datei1); // Temporary wird erzeugt und sogleich wieder zerstört
// Hier ist Datei1 in einem zwar definierten, aber unbrauchbaren Zustand und löst spätestens beim Zerstören einen Fehler aus
Ebenso ist es schleierhaft, weswegen der Typ FILE ohne vorangehender Namensbereichqualifizierung (std::) benutzt wurde.
Ich schlage folgende Alternative vor, die auch den Bezug zur Ausnahmesicherheit verdeutlicht:
#include <iostream>
#include <stdexcept>
// Eine Klasse, die ihren Speicher am Ende ihrer Existenz selbst freigibt
template<typename T>
class Ressource
{
	T* Zeiger;
	// Kopierkonstruktor und Zuweisungsoperator werden private deklariert, damit sie nicht zugänglich sind
	Ressource(Ressource const&); // oder mit C++11: = delete
	Ressource& operator= (Ressource const&); // oder mit C++11: = delete
public:
	explicit Ressource(T* Zeiger) : Zeiger(Zeiger) { }
	~Ressource() { delete Zeiger; }
	T& Zugriff() const { return *Zeiger; }
};
// Funktion, die u.U. eine Ausnhame auslöst
void f(int Wert)
{
	if(Wert == 0)
		throw std::invalid_argument("f(int): Wert darf nicht 0 sein");
}
int main()
{
	try
	{
		Ressource<int> SichereDaten(new int(7));
		SichereDaten.Zugriff() += 3;
		
		int* UnsichereDaten = new int(5);
		*UnsichereDaten *= 3;
		
		f(0);
		
		delete UnsichereDaten; // Wird nie erreicht, ergo wird UnsichereDaten nie freigegeben -> Speicherleck
		// SichereDaten wird dank RAII freigegeben, da die Ausnahme von f in diesem Gültigkeitsbereich nicht mehr abgefangen wurde
	}
	catch(std::invalid_argument const& Ausnahme)
	{
		std::cout << "Es ist eine Ausnahme aufgetreten:\n\t" << Ausnahme.what();
	}
}
--Asfdlol (Diskussion) 12:02, 15. Aug. 2014 (CEST)
thread_title:  Programmbeispiel