Frage

Ich arbeite an einer C ++ app, die einige Controller-Objekte intern hat, die regelmäßig erstellt und zerstört werden (unter Verwendung von neu). Es ist notwendig, dass dieser Controller selbst mit einem anderen Objekt zu registrieren (nennt sie es controllerSupervisor), und heben Sie die Registrierung selbst, wenn sie zerstört werden.

Das Problem, das ich jetzt bin vor geschieht, ist, wenn ich die Anwendung beenden: als Auftrag der Zerstörung nicht deterministisch ist, geschieht es so, dass die einzelne controllerSupervisor Instanz vor (etwas) zerstört wird der Controller selbst, und wenn sie nennen deregistrieren Methode in ihrer destructor, tun sie dies auf ein bereits zerstört Objekt.

Die einzige Idee, die ich mit so weit kam (eine große Kälte hat, so kann dies nicht viel bedeutet) ist nicht mit der controllerSupervisor als globaler Variable auf dem Stapel, sondern auf dem Heap (das heißt mit neu). Aber in diesem Fall habe ich keinen Platz, es zu löschen (das ist alles in einem 3rd-Party-Art Bibliothek).

Für Hinweise / Vorschläge auf, was mögliche Optionen würden geschätzt werden.

War es hilfreich?

Lösung

Sie können die Beobachter-Muster verwenden. Ein Controller kommuniziert es ist Supervisor, dass es zerstört werden wird. Und der Betreuer in Verbindung steht, das gleiche zu ihm dem Kind auf Zerstörung.

Hier finden Sie aktuelle http://en.wikipedia.org/wiki/Observer_pattern

Andere Tipps

Die Reihenfolge der Zerstörung von automatischen Variablen (die „normale“ lokale Variablen enthalten, die Sie in Funktionen verwenden) ist in der umgekehrten Reihenfolge ihrer Entstehung. So legen Sie die controllerSupervisor an der Spitze.

Reihenfolge der Zerstörung von Globals ist auch in der umgekehrten ihrer Entstehung, die in der Größenordnung hängt wiederum in dem sie definiert sind: Später definierte Objekte werden später erstellt. Aber Vorsicht:. Objekte definiert in verschiedenen CPP-Dateien (Übersetzungseinheiten) sind nicht garantiert in jedem festgelegten Reihenfolge erstellt

Ich denke, man beachten sollte, es zu benutzen, wie Mike empfohlen:

  1. Creation wird durch Verwendung des Singletonmuster erfolgt bei der ersten Verwendung (seit der Initialisierung der Reihenfolge der Objekte in verschiedenen Übersetzungseinheiten sind nicht definiert ist), durch einen Zeiger auf eine Funktion statischen Supervisor-Objekt zurück.
  2. Der Supervisor ist in der Regel zerstört (die Regeln über die Zerstörung der Statik in Funktionen). Controller deregistrieren eine statische Funktion des Supervisors verwendet. Dass man überprüft, ob die Aufsichtsbehörde bereits zerstört (einen Zeiger für != 0 Überprüfung). Wenn ja, dann wird nichts getan. Andernfalls wird der Supervisor mitgeteilt wird.

Da ich dort vorstellen, dass ein Supervisor ohne Controller könnte verbunden ist (und wenn auch nur vorübergehend), ein Smart-Pointer nicht verwendet werden, um den Supervisor automatisch zu zerstören.

Es ist im Grunde ein ganzes Kapitel zu diesem Thema in Alexandrescu Modern C ++ Entwurf (Chaper 6, Singletons). Er definiert eine Singleton-Klasse, die Abhängigkeiten verwalten, auch zwischen Singletons selbst.

Das ganze Buch ist übrigens sehr zu empfehlen.

Ein paar Vorschläge:

  • die controllerSupervisor ein Singleton machen (oder in einem Singleton-Objekt wickeln Sie zu diesem Zweck erstellen), die über eine statische Methode zugegriffen wird, die einen Zeiger zurückgibt, dann werden die dtors der registrierten Objekte können die statische Accessor nennen (die im Fall der Anwendung Abschaltung und die controllerSupervisor wurde, wird NULL zurückgeben zerstört) und die Objekte vermeiden können die de-Register-Methode in diesem Fall aufgerufen wird.

  • Erstellen Sie die controllerSupervisor auf dem Heap mit neuen und so etwas wie boost::shared_ptr<> verwenden, um ihre Lebensdauer zu verwalten. Hand aus dem shared_ptr<> in dem Singletons statischen Accessormethode.

GNU gcc / g ++ bietet nicht tragbare Attribute für Typen, die sehr nützlich sind. Eines dieser Attribute ist init_priority , das die Reihenfolge, in der globalen Objekte definiert sind konstruiert und, als Folge, die umgekehrte Reihenfolge, in der sie zerstört zu bekommen. Von dem Mann:

  

init_priority (Priorität)

 In Standard C++, objects defined at namespace scope are guaranteed
 to be initialized in an order in strict accordance with that of
 their definitions _in a given translation unit_.  No guarantee is
 made for initializations across translation units.  However, GNU
 C++ allows users to control the order of initialization of objects
 defined at namespace scope with the init_priority attribute by
 specifying a relative PRIORITY, a constant integral expression
 currently bounded between 101 and 65535 inclusive.  Lower numbers
 indicate a higher priority.

 In the following example, `A' would normally be created before
 `B', but the `init_priority' attribute has reversed that order:

      Some_Class  A  __attribute__ ((init_priority (2000)));
      Some_Class  B  __attribute__ ((init_priority (543)));

 Note that the particular values of PRIORITY do not matter; only
 their relative ordering.

Sie können eine der folgenden Aktionen je nach den Umständen.

  1. Verwenden Sie die Beobachter-Muster von gurin vorgeschlagen, wie. Grundsätzlich ist der Supervisor informiert die Controller, dass es nach unten geht ...
  2. Haben die Supervisor „eigene“ die Controller und für ihre Zerstörung verantwortlich sein, wenn es untergeht.
  3. Halten Sie die Controller in shared_pointers so wer untergeht zuletzt die wirkliche Zerstörung tun wird.
  4. Verwalten sowohl die (Smart-Pointer auf die) Controller und der Supervisor auf dem Stapel, die Ihnen erlaubt, die Reihenfolge der Zerstörung zu bestimmen
  5. andere ...

Sie bei Verwendung der Anzahl der registrierten Controller als Wächter für die tatsächliche Löschung aussehen könnte.

Die Lösch Anruf ist dann nur eine Anfrage und Sie müssen warten, bis die Controller abzumelden.

Wie bereits erwähnt dies eine Verwendung des Betrachters Muster ist.

class Supervisor {
public:
    Supervisor() : inDeleteMode_(false) {}

    void deleteWhenDone() {
        inDeleteMode_ = true;
        if( controllers_.empty()){
            delete this;
        }
    }

    void deregister(Controller* controller) {
        controllers_.erase(
            remove(controllers_.begin(), 
                        controllers_.end(), 
                        controller));
        if( inDeleteMode_ && controllers_.empty()){
            delete this;
        }
    }


private:

    ~Supervisor() {}
    bool inDeleteMode_;
    vector<Controllers*> controllers_;
};

Supervisor* supervisor = Supervisor();
...
supervisor->deleteWhenDone();

Es ist nicht gerade elegant, aber man könnte so etwas tun:

struct ControllerCoordinator {
    Supervisor supervisor;
    set<Controller *> controllers;

    ~ControllerDeallocator() {
        set<Controller *>::iterator i;
        for (i = controllers.begin(); i != controllers.end(); ++i) {
            delete *i;
        }
    }
}

New global:

ControllerCoordinator control;

Wo man auch einen Controller konstruieren, fügen control.supervisor.insert(controller). Überall, wo Sie ein zerstören, fügen control.erase(controller). Sie könnten eine globale Referenz, indem control.supervisor den control. Präfix vermeiden können.

Das Supervisor Mitglied des Koordinators wird erst nach den destructor Läufen zerstört werden, so dass Sie garantiert sind, dass die Aufsichtsbehörde wird die Controller überleben.

Wie wäre es der Vorgesetzte nehmen mit Sorgfalt die Controller von zerstörenden?

Ok, wie an anderer Stelle vorgeschlagen machen die Vorgesetzten ein Singleton (oder Ähnlich gesteuertes Objekt, das heißt, zu einer Sitzung scoped).

Verwenden Sie die entsprechenden Wachen (theading, etc) um Singletons, falls erforderlich.

// -- client code --
class ControllerClient {
public:
    ControllerClient() : 
        controller_(NULL)
        {
            controller_ = Controller::create();
        }

    ~ControllerClient() {
        delete controller_;
    }
    Controller* controller_;
};

// -- library code --
class Supervisor {
public: 
    static Supervisor& getIt() {        
        if (!theSupervisor ) {
            theSupervisor = Supervisor();
        }
        return *theSupervisor;
    } 

    void deregister(Controller& controller) {
        remove( controller );
        if( controllers_.empty() ) {
            theSupervisor = NULL;
            delete this;
        }       
    }

private:    
    Supervisor() {} 

    vector<Controller*> controllers_;

    static Supervisor* theSupervisor;
};

class Controller {
public: 
    static Controller* create() {
        return new Controller(Supervisor::getIt()); 
    } 

    ~Controller() {
        supervisor_->deregister(*this);
        supervisor_ = NULL;
    }
private:    
    Controller(Supervisor& supervisor) : 
        supervisor_(&supervisor)
        {}
}

Während hässlich könnte dies die einfachste Methode sein:

hat gerade einen Versuch fangen um den austragen Anruf. Sie müssen nicht viel Code ändern und da die App bereits heruntergefahren ist es keine große Sache. (Oder gibt es andere Ratifikationen für die Reihenfolge der Abschaltung?)

Andere haben bessere Konstruktionen ausgeführt hat, aber dieses ist einfach. (Und hässlich)

Ich ziehe die Beobachter-Muster auch in diesem Fall.

Sie können Ereignisse verwenden Zerstörung Controller zu signalisieren,

In WaitForMultipleObjects in destructor Supervisor, die bis alle Controller zerstört werden warten.

In destructor von Controllern können Sie Controller Exit-Ereignis ausgelöst.

Sie müssen halten globaler Array von Exit-Ereignisse für jeden Controller behandelt.

Machen Sie die cotrol Supervisor ein Singelton. Stellen Sie sicher, dass ein Steuer Konstruktor den Supervisor während der Bauphase wird (nicht Nachwort). Damit ist gewährleistet, dass die Steuer Supervisor vollständig vor der Steuerung aufgebaut ist. Thuse der destructor wird, bevor die Steuer Supervisor destructor genannt werden.

class CS
{
    public:
        static CS& getInstance()
        {
            static CS  instance;
            return instance;
        }
        void doregister(C const&);
        void unregister(C const&);
    private:
        CS()
        {  // initialised
        }
        CS(CS const&);              // DO NOT IMPLEMENT
        void operator=(CS const&);  // DO NOT IMPLEMENT
 };

 class C
 {
      public:
          C()
          {
              CS::getInstance().doregister(*this);
          }
          ~C()
          {
              CS::getInstance().unregister(*this);
          }
 };

Der C ++ Standard spezifiziert die Reihenfolge der Initialisierung / Zerstörung, wenn die Variablen in Frage alle in einer Datei passen ( „Übersetzungseinheit“). Alles, was mehr als eine Datei wird nicht tragbaren umspannt.

Ich würde zweite die Vorschläge jeden Controller das Supervisor zerstören haben. Dies ist sicher im Sinne Thread, der nur das Supervisor jemand selbst zu zerstören erzählt (niemand zerstört sich auf ihre eigenen), so gibt es keine Race-Bedingung. Sie würden auch keine Deadlock Möglichkeiten vermeiden müssen. (Hinweis: Stellen Sie sicher, dass die Controller selbst zerstören können, sobald sie gesagt haben, ohne alles , um sonst vom Betreuer)


Es ist möglich, diesen Thread-sicher zu machen, auch wenn die Steuerung vor dem Ende des Programms zerstört werden muss (das heißt, wenn Controller kurzlebig sein können) dann sie (oder jemand anderes).

Erstens kann es nicht eine Race-Bedingung sein zu kümmern, wenn ich mich entscheide, mich zu zerstören und Mikrosekunden später entscheidet der Betreuer, mich zu vernichten und sagt es mir.

Zweitens, wenn Sie diesen Rennen Zustand besorgt sind Sie es durch beheben können, sagen wir, zu verlangen, dass alle Zerstörung Anforderungen durch Betreuer gehen. Ich will mich selbst zerstören, entweder das Supervisor sagen Sie mir zu sagen, oder ich registriere, dass Absicht mit dem Betreuer. Wenn jemand anders - einschließlich der Betreuer -. Will mich zerstört, sie tun, dass durch den Betreuer

Als ich die Überschrift auf diese Frage habe ich gelesen, mich sofort gefragt: „Wenn es eine Möglichkeit gäbe, dass jedes Objekt sicherstellen konnte, dass es zerstört wurde (zerstört?) Letzte, was dann passieren würde, wenn zwei Objekte diese Methode angenommen?“

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