2015-10-23 12 views
6

Wenn die Ressource, die man zum Aufbau eines Objekts benötigt, fehlschlagen kann, ist RAII möglich, wenn Ausnahmen durch einen lokalen Codierungsstandard verboten sind?Ist es möglich, RAII ohne Ausnahmen zu machen?

Wenn ja, was ist die kanonische Art, in diesem Fall den Ausfall der Ressourcenakquisition zu behandeln?

+1

Wenn ich Ausnahmen aus irgendeinem Grund nicht verwenden kann, erstelle ich Initialisierungsmethoden für Objekte, die Konstruktion fehlschlagen können. Also würde der Konstruktor nichts anderes als eine triviale Einrichtung tun. Dann kann ich eine Init-Methode haben, die die tatsächliche Ressourcenerfassung durchführt und dementsprechend true/false zurückgibt. –

+2

Sagen deine Richtlinien nichts über solche Situationen? Es ist * ein sehr allgemeines C++ Idiom schließlich. Und wenn die Richtlinien dies zulassen, könnten Sie immer ein System verwenden, das der [Standard-E/A-Bibliothek] (http://en.cppreference.com/w/cpp/io) und seinen Streams, z. dass die Streams in Bedingungen verwendet werden können, um nach Fehlern zu suchen, oder wie die Dateiströme eine 'is_open'-Funktion haben. –

+0

@JoachimPileborg Ich denke nicht, dass es in den Richtlinien berücksichtigt wurde, sie können eine Aktualisierung erfordern (obwohl es ein eingebettetes Echtzeitsystem ist und Ausnahmen nicht gemocht werden!) Unabhängig davon ist mein Gefühl, dass es nicht möglich ist, also nur für Um dieser Frage willen möchte ich mich bestätigt fühlen! :-) – Joe

Antwort

6

Ich würde nicht mit dem ungültigen Objekt Ansatz im Allgemeinen gehen, weil ich dies als schlechtes Designs in Betracht ziehen würde. Nach der Konstruktion muss das Objekt in einem Zustand sein, in dem die Invarianten eingerichtet sind (das ist der einzige Zweck, den ein Konstruktor bedienen sollte). Betrachten Sie eine Klasse strange_vector, die etwas wie std::vector implementiert, aber nach dem Aufruf von strange_vector<int>(10, 0) wäre das Objekt in einem unbrauchbaren Zustand, weil die Zuweisung fehlgeschlagen ist.

Stattdessen würde ich die Konstrukteursprivat erklären und eine Factory-Methode verwenden, die eine optionale zurückgibt:

class file 
{ 
public: 
    ~file() {fclose(m_file);} 

    static std::optional<file> open(std::string const& filename) 
    { 
     auto f = fopen(filename.c_str(), "r"); 
     if (f) 
     { 
      return std::make_optional<file>(f); 
     } 
     else 
     { 
      return std::nullopt; 
     } 
    } 

private: 
    file(FILE* file); 
    FILE* m_file; 
}; 

Einer der größten Vorteile der Ausnahmebehandlung ist (neben Entkopplung der Fehlerbehandlung und normalen Codepfad), dass man nicht ignoriere sie versehentlich. Wenn Sie das möchten, könnten Sie Ihre eigene Klasse ähnlich optional erstellen, die, wenn sie nicht mit einem gültigen Objekt initialisiert wird, eine Fehlermeldung protokolliert und Ihr Programm (oder jede andere angemessene Fehlerbehandlung) beendet. Ich denke, es gibt eine talk from A. Alexandrescu about systematic error handling, wo er eine Klasse Expected<T> implementiert, die entweder einen Wert des Typs T oder eine Ausnahme enthält. Sie können dies als Basis verwenden und statt der Ausnahme fügen Sie dort Ihre Fehlerbehandlung hinzu.

std::optional ist noch nicht Teil des Standards, aber Sie können Implementierungen entweder als Teil der letzten Compiler, in Boost oder in anderen Bibliotheken erhalten.

+0

Sie gemischt lokalen 'f' und Mitglied' m_file' up, nicht wahr? –

+0

@ MatthäusBrandl Ja, danke, dass du das gesehen hast. – Jens

1

Sie können immer eine bool valid(void) Methode erstellen. Der Konstruktor kann dann die entsprechende Bedingung setzen und in Ihrem Code können Sie nach der Erstellung überprüfen, ob dies funktioniert oder nicht.

class foo 
{ 
public: 
    foo(const char *Filename) 
    { 
     mValid = false; 

     if(fopen(Filename) == NULL) 
      return; 

     mValid = true; 
    } 

    bool valid(void) { return mValid; } 

    private: 
     bool mValid; 
}; 

void myfunc(void) 
{ 
    foo fl("myfile"); 
    if(!fl.valid()) 
    { 
     printf("Error\n"); 
     return; 
    } 
} 
+0

Ich mag diesen Ansatz wirklich nicht. Was sollte passieren, wenn ich eine Methode von foo aufrufen, die die Datei verwendet, aber die Initialisierung fehlgeschlagen ist? Konstruktoren, die die Invarianten des Objekts nicht festgelegt haben, sind ein No-Go. – Jens

+0

Wenn Sie eine Methode aufrufen, die foo verwendet, sollte intern validiert werden, dass die Datei tatsächlich gültig ist. Nun, es ist hässlich, aber es garantiert zumindest, dass das Objekt selbst korrekt funktioniert. – Devolus

+0

Damit wird die Überprüfung für jeden Methodenaufruf hinzugefügt. Ich bin mir nicht sicher, dass das OP dies in seiner eingebetteten Domäne will, und ich denke nicht, dass ich es im Allgemeinen, z. wenn eine Vektorklasse mit dieser Methode, die für jeden Zugriff prüft ... – Jens