Virtuellen Destruktor mit virtuellen Mitgliedern, die in C++11
In diesen Folien über C++11/14 standard, auf Folie 15 der Autor schreibt, dass "viele klassische Codierung Regeln [sind] nicht mehr anwendbar," in C++11. Er schlägt vor, eine Liste von drei Beispielen, und ich Stimme zu, die Regel der Drei und das Speicher-management.
Jedoch seine zweite Beispiel ist die "Virtuellen Destruktor mit virtuellen Mitglieder" (nur, dass). Was bedeutet es? Ich weiß, man muss erklären, wie virtuelle Basisklasse destructor, um die richtige Destruktor, wenn wir so etwas wie
Base *b = new Derived;
...
delete b;
Dies ist gut erklärt hier: Wenn man virtuelle Destruktoren?
Aber es ist nutzlos, jetzt in C++11 zu erklären, Ihre virtuelle Destruktor, wenn Sie virtuelle Mitarbeiter?
- Dies ist nur eine Vermutung, aber bei der Verwendung von smart-Pointer, können Sie ordnen die Dinge so, dass der richtige Destruktor aufgerufen wird, auch wenn die Basis-Destruktor nicht virtuell. Dies geschieht aus der box mit
shared_ptr
. - Die Aussagen zu der Wirkung, dass die in der Regel aus drei oder die Notwendigkeit für einen virtuellen Destruktor nicht mehr notwendig sind, sind schlicht falsch. Keine der neuen Funktionen haben sich etwas geändert in dieser Hinsicht.
- Über die Regel der Drei, der Autor könnte auch bedeuten, dass es veraltet ist, da nun es ist die Regel der Vier/Fünf. Für die Regel der Null, ich glaube wirklich, dass es sinnvoll ist, aber unter der Bedingung, dass Ihre Klassen verwenden, die RAII-Prinzip für alle Ressourcen, die Sie besitzen.
- Nicht wirklich. Die meisten Klassen brauchen nicht die zusätzliche Komplexität der Unterstützung bewegen. (Und Klasse, die Ressourcen verwendet, die muss freigegeben werden, richtig arbeiten benötigen in der Regel viel mehr als das, was die üblichen RAII-Klassen tun können.)
- Sie fehlt der Punkt. Wenn alle Ihre nicht-triviale Mitglieder haben eine richtige Destruktor oder ein RAII-Typ mit einem geeigneten deleter (ob das gleich löscht den Speicher oder führt komplizierter cleanup), dann gibt es keine zusätzliche Komplexität zu unterstützen verschieben. Sie sagen einfach
Foo(Foo&&) = default;
und es Einfach Funktioniert™. Und weil alle Ihre Mitglieder sich selbst reinigen, können Sie auch Standard-Destruktor. Es erfordert einen anderen Ansatz zur Klasse design, aber das ist der Ansatz von Prof. Sommerlad befürwortet in diesen Folien. (Nicht sicher über die virtuelle bisschen aber ich werde ihn Fragen.) - die Antwort von PeterSom scheint mir perfekt...
- Es ist in der Tat! Ich wurde gefragt, für details über diese gleitet, und eine Antwort bekommen, von dem Autor selbst. 🙂 Ich werde belohnen Sie seine Antwort mit der bounty und die beste Antwort, sobald die bounty fertig (morgen). Bezweifle ich, es wird besser beantworten...
Du musst angemeldet sein, um einen Kommentar abzugeben.
Als Autor der Seiten ich werde versuchen, das zu klären.
Wenn Sie code schreiben, der explizit die Gewährung einer
Derived
Instanz mitnew
und zerstören es mitdelete
mit base class pointer ist, dann müssen Sie definieren einevirtual
Destruktor, ansonsten Sie am Ende mit unvollständig Zerstörung derDerived
Instanz. Allerdings empfehle ich zu verzichtennew
unddelete
vollständig und verwenden Sie ausschließlichshared_ptr
für die Bezugnahme auf heap-allocated polymorphe Objekte, wieDiese Weise wird der shared pointer verfolgt die original-Destruktor verwendet werden, auch wenn
shared_ptr<Base>
wird verwendet, um Sie zu vertreten. Einmal, das Letzte beziehtshared_ptr
geht out of scope oder zurückgesetzt wird,~Derived()
aufgerufen werden und der Speicher freigegeben. Daher brauchen Sie nicht zu machen~Base()
virtuellen.unique_ptr<Base>
undmake_unique<Derived>
nicht bieten dieses feature, weil Sie nicht die mechanik dershared_ptr
mit Bezug auf die deleter, weil der einzige Zeiger ist viel einfacher und soll für den geringsten overhead und damit ist nicht die Speicherung der zusätzlichen Funktion Zeiger benötigt für die Streicher. Mitunique_ptr
die deleter-Funktion ist Teil der Art und Weise ein uniqe_ptr mit einer deleter bezogen auf~Derived
wäre nicht kompatibel mit einerunique_ptr<Base>
mit dem Standard-deleter, das wäre falsch, für die eine abgeleitete Instanz sowieso, wenn~Base
war nicht virtuell.Die einzelnen Vorschläge, die ich machen, sind dazu gedacht, um einfach zu sein zu Folgen und alle Folgen zusammen. Sie versuchen zu produzieren, einfacher code, indem alle Ressourcen-management erfolgen durch die Bibliothek-Komponenten und den vom compiler generierten code.
Definition einer (virtuellen) Destruktor in einer Klasse, werden die es verbieten, dass ein compiler-zur Verfügung gestellt move-Konstruktor/Zuweisungsoperator und verbieten können auch einen compiler zur Verfügung gestellt copy-Konstruktor/Zuweisungsoperator in zukünftige Versionen von C++. Er hat sich einfach mit
=default
, aber sieht immer noch wie eine Menge boilerplate-code. Und das beste code ist der code, den Sie nicht schreiben müssen, denn es kann nicht falsch sein (ich weiß, es gibt noch Ausnahmen zu dieser Regel).Zusammenfassen "nicht definieren, eine (virtuelle) Destruktor" als eine Ergänzung zu meiner "Regel-der-Null":
Immer, wenn Sie entwerfen eine polymorphe (OO) - Klasse Hierarchie in der modernen C++ und wollen/müssen auf allocate its-Instanzen auf dem heap, und der Zugang zu Ihnen durch ein base class pointer verwenden
make_shared<Derived>()
zu instanziieren Sie undshared_ptr<Base>
um Sie ringsherum zu behalten. Dies ermöglicht Ihnen, halten die "Rule of Zero".Dies bedeutet nicht, Sie muss weisen polymorphe Objekte auf dem heap. Zum Beispiel, die Definition einer Funktion, die ein
(Base&)
als parameter, aufgerufen werden können mit einem lokalenDerived
variable ohne Probleme und verhält sich polymorph, mit Bezug auf virtuelle member-Funktionen vonBase
.Meiner Meinung nach dynamischer OO-Polymorphismus ist stark überstrapaziert in vielen Systemen. Wir sollten nicht Programm wie Java, wenn, die wir verwenden, C++, es sei denn, wir haben ein problem, wo dynamischen Polymorphismus mit heap zugeordneten Objekten ist die richtige Lösung.
Base
ist eine Basis-KlasseDerived
meine Argumente sind noch gültig. Allerdings, wennBase
ist völlig unabhängig vonDerived
dann sollte das auch nicht kompilieren.Ich denke, dass dies zu tun ist mit der "rule of zero" erwähnt an anderer Stelle in der Präsentation.
Wenn Sie nur die automatische member-Variablen (D. H. die Nutzung
shared_ptr
oderunique_ptr
für Mitglieder, die sonst rohe Zeiger), dann brauchen Sie nicht zu schreiben, Ihre eigenen kopieren oder verschieben von Konstruktoren oder Operatoren -- der compiler-vorausgesetzt defaults optimal sein wird. Mit in-class-Initialisierung, die Sie nicht benötigen einen Standard-Konstruktor entweder. Und schließlich müssen Sie nicht schreiben Sie einen Destruktor an alle, virtuellen oder nicht.default
(scottmeyers.blogspot.fr/2014/03/...). So ist nach dieser geänderten Regel von Null, Schätze ich, dass man immer noch erklären müssen das Basis-Destruktor werden virtuelle.shared_ptr
zu zeigen, um polymorphe Objekte? OK, zwar bin ich immer noch glücklicher, wenn die definition einer Klasse ist korrekt, in sich selbst, ohne sich auf die Benutzer, die in ein bestimmtes idiom. Diese Benutzer können manchmal so seltsame Sachen...Dem Papier verbunden zeigt den relevanten code:
Den gespeicherten deleter ist
std::default_delete<Derived>
, die nicht erfordernBase::~Base
virtuellen werden.Jetzt können Sie bewegen dies zu einer
unique_ptr<Base>
, und es wird auch bewegen Sie denstd::default_delete<Derived>
ohne Konvertierung in einstd::default_delete<Base>
.std::shared_ptr
, nichtstd::unique_ptr
.shared_ptr
für die einfache OO-case, aber wasunique_ptr
bietet, ist in der Tat unzureichend.typeid
unddynamic_cast
)make_shared
zu halten overhead.