Frage

Da es keine finally in C ++ Sie haben die RAII verwenden Design-Muster statt, wenn Sie Ihren Code wollen Ausnahme sicher sein. Eine Möglichkeit, dies zu tun, ist durch Verwendung der destructor einer lokalen Klasse wie folgt aus:

void foo() {
    struct Finally {
        ~Finally() { /* cleanup code */ }
    } finalizer();
    // ...code that might throw an exception...
}

Dies ist ein großer Vorteil gegenüber dem gerade nach vorne Lösung, weil Sie den Bereinigungscode 2 mal nicht schreiben müssen:

try {
    // ...code that might throw an exception...
    // cleanup code (no exception)
} catch (...) {
    // cleanup code (exception)
    throw;
}

Ein großer Nachteil der lokalen Klasse-Lösung ist, dass Sie nicht direkt lokale Variablen in dem Bereinigungscode zugreifen können. So ist es Ihr Code viel aufblasen, wenn Sie den Zugriff auf sie benötigen, unabhängig:

void foo() {
    Task* task;
    while (task = nextTask()) {
        task->status = running;
        struct Finally {
            Task* task;
            Finally(Task* task) : task(task) {}
            ~Finally() { task->status = idle; }
        } finalizer(task);
        // ...code that might throw an exception...
    }
}

Meine Frage ist also: Gibt es eine Lösung, die beide Vorteile in sich vereint? Damit Sie a) keine doppelten Code zu schreiben und b) lokale Variablen in der Bereinigungscode, wie task im letzten Beispiel, aber ohne einen solchen Code aufblasen zugreifen können.

War es hilfreich?

Lösung

Statt struct Finally definieren, könnten Sie Ihren Bereinigungscode in Abhängigkeit von der Klasse Task extrahieren und Lokis Scopeguard.

ScopeGuard guard = MakeGuard(&Task::cleanup, task);

Siehe auch diese DrDobb der Artikel und diese andere Artikel für mehr über ScopeGuards.

Andere Tipps

Ich glaube nicht, dass es ein sauberer Weg zu erreichen, was Sie zu tun versuchen, aber ich denke, das Hauptproblem mit dem ‚schließlich Ansatz‘ in Ihrem Beispiel ist ein unechter Trennung von Bedenken .

z. die Funktion foo () ist verantwortlich für die Konsistenz der Task-Objekt, das ist selten eine gute Idee, die Methoden der Aufgabe selbst sollten für die Einstellung der Status etwas Vernünftiges verantwortlich sein.

ich manchmal schon klar, es ist ein echter Bedarf an schließlich, und der Code ist offensichtlich nur ein einfaches Beispiel um einen Punkt zu zeigen, aber diese Fälle sind selten. Und ein bisschen mehr gekünstelt Code in seltenen Fällen ist akzeptabel für mich.

Was ich versuche zu sagen ist, sollten Sie nur selten eine Notwendigkeit für schließlich konstruiert und für die wenigen Fälle, in denen Sie das tun, würde ich sagen, verschwenden keine Zeit auf etwas schönere Art und Weise zu konstruieren. Es wird Sie ermutigen, nur endlich mehr zu verwenden, als Sie sollten wirklich ...

Das ist so eine hässliche Art und Weise tun: (kommen Sie aus Java)

Bitte lesen Sie diesen Artikel:
Hat C ++ Unterstützung 'endlich' Blöcke? (Und was ist das ‚RAH‘ Ich höre immer wieder über?)

Es erklärt, warum schließlich ist so ein hässliches Konzept und warum RIAA ist viel eleganter.

ich normalerweise etwas mehr wie folgt verwenden:

class Runner {
private:
  Task & task;
  State oldstate;
public:
  Runner (Task &t, State newstate) : task(t), oldstate(t.status); 
  {
    task.status = newstate;
  };

  ~Runner() 
  {
    task.status = oldstate;
  };
};

void foo() 
{
  Task* task;
  while (task = nextTask())
  {
    Runner r(*task, running);
            // ...code that might throw an exception...
  }
}

Wie andere gesagt haben, die „Lösung“ ist eine bessere Trennung von Bedenken. In Ihrem Fall ist, warum kann nicht die Aufgabe Variable kümmern, nachdem sich der Reinigung? Sollte eine Bereinigung auf es getan werden muss, dann sollte es keinen Zeiger, sondern ein RAH-Objekt.

void foo() {
//    Task* task;
ScopedTask task; // Some type which internally stores a Task*, but also contains a destructor for RAII cleanup
    while (task = nextTask()) {
        task->status = running;
        // ...code that might throw an exception...
    }
}

Smart-Pointer kann sein, was Sie in diesem Fall benötigen (boost :: shared_ptr wird den Zeiger standardmäßig löschen, aber Sie können stattdessen Funktionen benutzerdefinierte deleter angeben, die stattdessen beliebige Bereinigung takss durchführen können. Für RAH auf Zeiger, das ist in der Regel, was Sie wollen.

Das Problem ist nicht das Fehlen eines schließlich Schlüsselwort ist, ist es, dass Sie rohe Zeiger verwenden, die RAH nicht umsetzen kann.

Aber in der Regel sollte jeder Art wissen, wie nach sich aufzuräumen. Nicht nach jedem Objekt, das in Umfang war, als die Ausnahme ausgelöst wurde (was schließlich tut, und das, was Sie versuchen zu tun), kurz nach mir. Und wenn jedes Objekt, das der Fall ist, dann müssen Sie nicht den großen Fang-all „aufzuräumen nach jedem Objekt in scope“ Funktion überhaupt.

ich zu C ++ kam von Delphi, damit ich weiß, wovon ich rede. ICH HASSE endlich !!! Es ist hässlich . Ich wirklich glaube nicht, dass C ++ schließlich fehlt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top