Frage

Ich habe die letzten 4 Jahre in C # verbrachte so in aktuellen Best Practices und gängige Entwurfsmuster in C ++ Ich bin interessiert. Betrachten Sie das folgende Teil Beispiel:

class World
{
public:
    void Add(Object *object);
    void Remove(Object *object);
    void Update();
}

class Fire : Object
{
public:
    virtual void Update()
    {
        if(age > burnTime)
        {
            world.Remove(this);
            delete this;
        }
    }
}

Hier haben wir eine Welt, die für eine Reihe von Objekten verwalten und sie regelmäßig zu aktualisieren. Feuer ist ein ein Objekt, das in der Welt unter vielen verschiedenen Umständen hinzugefügt werden könnte, aber in der Regel von einem anderen Objekt bereits in der Welt. Feuer ist das einzige Objekt, das weiß, wann es so ausgebrannt ist zur Zeit habe ich es selbst zu löschen. Das Objekt, das das Feuer geschaffen wird wahrscheinlich nicht mehr vorhanden oder relevant.

Ist das eine vernünftige Sache zu tun, oder ist es ein besseres Design, dass diese Objekte bereinigen würde helfen?

War es hilfreich?

Lösung

Das Problem dabei ist, dass Sie wirklich eine implizite Kopplung zwischen dem Objekt und der Weltklasse zu schaffen.

Wenn ich versuche, Update () außerhalb der Weltklasse zu nennen, was passiert? Ich könnte mich mit dem Objekt, das gelöscht beenden, und ich weiß nicht, warum. Es scheint, die Verantwortung nach oben schlecht gemischt ist. Das wird Sie Probleme, den Moment bewirken, dass die Brandklasse in einer neuen Situation verwenden Sie nicht gedacht hatte, wenn Sie diesen Code geschrieben haben. Was passiert, wenn das Objekt sollte von mehr als einer Stelle gelöscht werden? Vielleicht sollte es sowohl aus der Welt entfernt werden, die aktuelle Karte und das Inventar des Spielers? Ihre Update-Funktion, die sie aus der Welt entfernen, und dann das Objekt löschen, und das nächste Mal, wenn die Karte oder das Inventar zugreifen möchte das Objekt, Bad Things Happen.

Generell würde ich sagen, dass es für ein Update () Funktion ist sehr intuitiv ist, das Objekt zu löschen, es zu aktualisieren ist. Ich würde auch sagen, es ist nicht intuitiv für ein Objekt selbst zu löschen. Das Objekt sollte eher eine Art Art und Weise haben, ein Ereignis zu feuern sagen, dass es brennt beendet, und alle Interessierten können nun auf das handeln. Zum Beispiel, indem sie aus der Welt zu entfernen. Für sie zu löschen, denken in Bezug auf das Eigentum.

Wer besitzt das Objekt? Die Welt? Das heißt, die Welt allein wird zu entscheiden, wann das Objekt stirbt. Das ist in Ordnung, solange die Bezugnahme der Welt auf das Objekt wird ein anderen Verweise auf sie zu überdauern. Glauben Sie, das Objekt selbst besitzen? Was bedeutet das überhaupt? Das Objekt sollte gelöscht werden, wenn das Objekt nicht mehr existiert? Macht keinen Sinn.

Wenn es aber keine klar definierten einzelnen Eigentümer, gemeinsame Verantwortung implementieren, beispielsweise ein Smart-Pointer-Implementierung Referenzzählung verwendet wird, wie boost::shared_ptr

Aber mit einer Memberfunktion auf dem Objekt selbst, das von dem Objekt zu entfernen fest einprogrammiert ist ein spezielle Liste, ob es existiert, und ob es existiert auch in jeder anderen Liste und auch das Objekt selbst unabhängig davon, welche Verweise auf sie existieren löschen, ist eine schlechte Idee.

Andere Tipps

Sie haben Update eine virtuelle Funktion gemacht, dass abgeleitete Klassen was auf die Implementierung von Update außer Kraft setzen kann. Dies bringt zwei große Risiken.

1). Eine überschriebene Implementierung erinnern kann eine World.Remove zu tun, kann aber die delete this vergessen. Der Speicher ist durchgesickert.

2.) Die überschriebene Implementierung ruft die Basisklasse Update, die ein delete this tut, aber dann geht mit mehr Arbeit, aber mit einem ungültigen this-Zeiger.

Betrachten Sie dieses Beispiel:

class WizardsFire: public Fire
{
public:
    virtual void Update()
    {
        Fire::Update();  // May delete the this-object!
        RegenerateMana(this.ManaCost); // this is now invaild!  GPF!
    }
}

Es gibt nichts wirklich falsch mit Objekten selbst in C ++, die meisten Implementierungen von Referenzzählung Löschen etwas ähnliches verwenden. Es wird jedoch als leicht esoterische Technik sieht auf, und Sie werden ein wirklich wachsames Auge auf den Eigentumsfragen halten müssen.

Die Lösch wird fehlschlagen und kann das Programm abstürzen, wenn das Objekt war nicht zugeordnet und mit neuen konstruiert. Dieses Konstrukt wird Sie instanziiert die Klasse auf dem Stapel zu verhindern oder zu statisch. In Ihrem Fall erscheinen Sie irgendeine Art von Zuordnungsschema werden. Wenn Sie dabei bleiben, könnte dies sicher sein. Ich würde sicherlich nicht tun es, aber.

Ich ziehe ein strengeres Eigentumsmodell. Die Welt sollte die Objekte darin besitzen und sich verantwortlich für die Reinigung. Entweder haben Welt entfernen tun oder haben update (oder eine andere Funktion) geben einen Wert zurück, der angibt, dass ein Objekt gelöscht werden soll.

Ich vermute auch, dass das Musters in dieser Art, die Sie zählen Ihre Objekte gehen zu wollen, verweisen mit baumelnden Zeigern zu vermeiden enden.

Es funktioniert sicherlich Objekte von new erstellt und wenn der Anrufer von Update richtig über dieses Verhalten informiert wird. Aber ich würde es vermeiden. In Ihrem Fall ist das Eigentum deutlich an der Welt, so würde ich die Welt löschen machen. Das Objekt nicht selbst erstellen, ich denke, es sollte auch nicht selbst löschen. Kann sehr überraschend, wenn Sie eine Funktion „Update“ rufen auf dem Objekt, aber dann plötzlich das Objekt vorhanden ist, wird nicht mehr ohne Welt (abgesehen von selbst etwas zu tun, von ihm aus seiner Liste entfernen - aber in einem anderen Rahmen Der Code ruft Update auf das Objekt wird nicht bemerken).

Einige Ideen auf diesem

  1. eine Liste von Objektreferenzen auf der Welt hinzufügen. Jedes Objekt in dieser Liste fehlt noch zur Entfernung. Dies ist eine übliche Technik, und ist in wxWidgets für Toplevel-Fenster verwendet, die geschlossen wurden, kann aber immer noch Nachrichten empfangen. In Ruhezeit, wenn alle Nachrichten verarbeitet werden, wird die Liste anhängiger verarbeitet und Objekte gelöscht werden. Ich glaube, dass Qt eine ähnliche Technik folgt.
  2. Sagen Sie der Welt, dass das Objekt gelöscht werden will. Die Welt wird richtig informiert und wird von jeder Sache kümmern, die getan werden muss. So etwas wie ein deleteObject(Object&) vielleicht.
  3. Fügen Sie für jedes Objekt eine shouldBeDeleted Funktion, die true zurückgibt, wenn das Objekt von seinem Eigentümer gelöscht werden möchte.

Ich würde es vorziehen Option 3. Die Welt würde aktualisieren nennen. Und danach sieht es aus, ob das Objekt gelöscht werden soll, und kann so tun -. Oder wenn er will, erinnert sich, dass tatsächlich durch das Hinzufügen dieses Objekt zu einer anstehenden Entfernungs Liste manuell

Es ist ein Schmerz in den Arsch, wenn Sie sich nicht sicher sein kann, wenn und wann nicht in der Lage sein, auf das Objekt zuzugreifen Funktionen und Daten. Zum Beispiel in wxWidgets, gibt es eine wxThread Klasse, die in zwei Modi betrieben werden. Einer dieser Modi (genannt „abnehmbar“) ist, dass, wenn seine Hauptfunktion zurückkehrt (und die Faden Ressourcen freigegeben werden sollen), löscht er selbst (den Speicher durch das Objekt wxThread belegt freizugeben) statt für den Besitzer des Threads warten Objekt Wartezeit rufen oder Funktion zu verbinden. Dies führt jedoch zu starken Kopfschmerzen. Sie können nie alle Funktionen auf sie nennen, weil es an irgendwelchen Umständen beendet worden sein könnte, und man kann es nicht nicht mit neuen erstellt haben. Ganz einige Leute mir gesagt, dass sie sehr viel Abneigung, dass das Verhalten von ihm.

Die Selbst Löschung von Referenz gezählt Objekt riecht, imho. Vergleichen wir:

// bitmap owns the data. Bitmap keeps a pointer to BitmapData, which
// is shared by multiple Bitmap instances. 
class Bitmap {
    ~Bitmap() {
        if(bmp->dec_refcount() == 0) {
            // count drops to zero => remove 
            // ref-counted object. 
            delete bmp;
        }
    }
    BitmapData *bmp;
};

class BitmapData {
    int dec_refcount();
    int inc_refcount();

};

Vergleichen Sie das mit sich selbst zu löschen refcounted Objekte:

class Bitmap {
    ~Bitmap() {
        bmp->dec_refcount();
    }
    BitmapData *bmp;
};

class BitmapData {
    int dec_refcount() {
        int newCount = --count;
        if(newCount == 0) {
            delete this;
        }
        return newCount;
    }
    int inc_refcount();
};

Ich denke, die ersten so viel schöner ist, und ich glaube, gut gestaltet Referenz gezählt Objekte tun nicht do „löscht diese“, weil es Kopplung erhöht: Die Klasse mit den Referenzdaten gezählt hat wissen, und erinnern, dass die Daten über löscht sich als Nebeneffekt der Dekrementieren des Referenzzählung. Beachten Sie, wie „bmp“ wird möglicherweise ein baumelnden Zeiger in ~ Bitmap Destruktor. Argumentieren, nicht zu tun, dass „diese löschen“ ist hier viel schöner.

Antwort auf eine ähnliche Frage "Was ist die Verwendung von löschen "

Das ist eine ziemlich häufige Implementierung Referenzzählung und ich verwendet habe, die erfolgreich vor.

Allerdings habe ich es auch zum Absturz gesehen. Ich wünschte, ich könnte die Umstände für den Absturz erinnern. @abelenky ist ein Ort, den ich gesehen habe es zum Absturz bringen.

Es könnte, wo Sie weitere Unterklasse Fire gewesen, doch nicht einen virtuellen Destruktor (siehe unten) zu erstellen. Wenn Sie nicht über einen virtuellen Destruktor haben, wird die Update() Funktion anstelle des entsprechenden destructor ~Fire() nennen ~Flame().

class Fire : Object
{
public:
    virtual void Update()
    {
        if(age > burnTime)
        {
            world.Remove(this);
            delete this; //Make sure this is a virtual destructor.
        }
    }
};

class Flame: public Fire
{
private: 
    int somevariable;
};

Andere haben erwähnt Probleme mit „diese löschen.“ Kurz gesagt, schafft seit „World“ die Schaffung von Feuer, es sollte auch deren Löschung verwalten.

Ein weiteres Problem ist, wenn die Welt mit Feuer endet noch brennt. Wenn dies der einzige Mechanismus ist, durch die ein Feuer zerstört werden kann, dann könnte man mit einem verwaisten oder Müll Feuer enden.

Für die Semantik Sie wollen, hätte ich eine „aktive“ oder „lebendig“ Flagge in Ihrer „Fire“ Klasse (oder Objekt falls zutreffend). Dann würde die Welt gelegentlich ihren Bestand an Objekten überprüfen, und loszuwerden diejenigen, die nicht mehr aktiv ist.

-

Ein weiterer Hinweis ist, dass der Code geschrieben, wie hat Feuer privat von Object erben, da dies der Standard ist, auch wenn es viel seltener als öffentliche inheiritance ist. Sie sollten wahrscheinlich die Art der Vererbung explizit machen, und ich vermute, dass Sie wirklich öffentlich inheritiance wollen.

Es ist nicht generell eine gute Idee, ein „löschen diese“ wenn es nicht notwendig ist oder in einer sehr einfachen Art und Weise zu tun. In Ihrem Fall sieht es aus wie die Welt kann nur das Objekt löschen, wenn es entfernt wird, es sei denn, andere Abhängigkeiten sind wir nicht zu sehen (in diesem Fall Ihre Lösch Anruf Fehler verursachen, da sie nicht angemeldet sind). Halten Sie Eigentum außerhalb Ihres Objekts ermöglicht Ihr Objekt besser gekapselten und stabiler zu sein. Das Objekt wird nicht an einem gewissen Punkt ungültig wird, nur weil sie gewählt haben, sich zu löschen

Ich glaube nicht, das es etwas grundsätzlich falsch mit einem Objekt zu löschen selbst, sondern ein anderes mögliches Verfahren wäre die Welt für Objekte als Teil verantwortlich haben Löschen :: entfernen (alle Entfernten Objekte wurden ebenfalls gelöscht angenommen wird.)

löscht dies sehr riskant ist. Es ist nichts „falsch“ mit ihm. Es ist legal. Aber es gibt so viele Dinge, die schief gehen können, wenn Sie es verwenden, dass es sollte sparsam verwendet werden.

Betrachten wir den Fall, wo jemand offen Zeiger oder Verweise auf das Objekt hat, und dann geht es und löscht sich selbst.

In den meisten Fällen, ich denke, das Objekt Lebensdauer von demselben Objekt verwaltet werden soll, die es erstellt. Es macht es einfach leichter zu wissen, wo Zerstörung auftritt.

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