Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!
Werbeanzeige
C-/C++-Quelltext |
|
1 |
-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-missing-braces |
C-/C++-Quelltext |
|
1 2 3 4 |
warning: 'Foo' has no out-of-line virtual method definitions; its vtable will be emitted in every translation unit [-Wweak-vtables] class Foo : public Master<int> { ^ |
C-/C++-Quelltext |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class BaseMaster { // attrs, stuff, ... nix virtuales public: BaseMaster(); virtual ~BaseMaster(); // ~BaseMaster(){} in .cpp }; template<class T> class Master : public BaseMaster { // ... nix virtuales hier // attrs, shared_ptr<T>, ... public: Master(){} virtual ~Master() = default; }; class Foo : public Master<U> { public: Foo(); }; |
Da hate's: Du benutzt den Destruktor!Zitat
Kein new oder delete, alles über STL.
C-/C++-Quelltext |
|
1 2 3 4 5 6 7 |
class MyBaseObject { public: // ... protected: std::string name_; size_t id_; }; |
C-/C++-Quelltext |
|
1 2 3 4 5 |
class MyBaseObject { public: virtual ~MyBaseObject() = default; // ... }; |
C-/C++-Quelltext |
|
1 2 |
virtual ~Foo(); ~Foo() override; |
C-/C++-Quelltext |
|
1 2 3 4 |
int main(int _argc, char** _argv) noexcept { asm volatile("lock cmpxchg8b %eax"); return 0; } // ::main |
Der Compiler fügt dann automatisch die Standard-Implementierung ein. Ist das gleiche, als würdest du virtual ~MyBaseObject() {} tippen, bloß dass default besser die Absicht vermittelt.
Zudem kannste nicht einfach eine virtuelle Funktion einer Basisklasse durch eine nicht-virtuelle ersetzen, wie du es in Foo tust!
Zitat
Klar die BaseKlasse muss einen haben wenn diese von einer anderen Klasse geerbt wird, (oder nicht?)
Es ist mal wieder eine bekannte Frage die immer wieder gestellt wird und ich die Antwort eigentlich kennen müsset.
"Wann benutzt man ein virtual dtor?"
Klar die BaseKlasse muss einen haben wenn diese von einer anderen Klasse geerbt wird, (oder nicht?)
C-/C++-Quelltext |
|
1 2 |
Base* bla = new Derived; delete bla; |
C-/C++-Quelltext |
|
1 |
std::unique_ptr<Base> bla(new Derived); |
Ich benutzt NIE den dtor, sonder deklariere immer ein leeren virtual dtor.
Der Compiler fügt dann automatisch die Standard-Implementierung ein. Ist das gleiche, als würdest du virtual ~MyBaseObject() {} tippen, bloß dass default besser die Absicht vermittelt.
Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von »dot« (31.03.2015, 19:26)
C-/C++-Quelltext |
|
1 2 3 4 |
int main(int _argc, char** _argv) noexcept { asm volatile("lock cmpxchg8b %eax"); return 0; } // ::main |
Community-Fossil
Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer
Solche oder ähnliche Aussagen lese ich von dir erstaunlich oft. Du verwendest nur selten Polymorphie, oder? Während das in vielen Business-Anwendungen tatsächlich nicht oft benötigt wird, ist es bei Spielen doch schon eher mal an der Tagesordnung.Nur bei Klassen, wo die Möglichkeit eines delete über einen Basepointer tatsächlich gegeben sein muss (meiner Erfahrung nach eher selten), würde ich einen public virtual Desktruktor definieren.
Nein; leider wird sehr oft gelehrt, dass eine Klasse, von der geerbt wird, einen virtuellen Destruktor haben sollte. Das ist allerdings nicht wirklich notwendig, potentiell performanceschädlich und imo als Bad Practise einzustufen. Ein virtueller Destruktor wird (wie das eben generell mit virtuellen Methoden so ist) nur dann benötigt, wenn auf irgendeine Art der Destruktor einer abgeleiteten Klasse über einen Basisklassenzeiger augerufen werden soll; z.B. weil ein Objekt einer abgeleiteten Klasse über einen Basisklassenzeiger deleted werden soll
Ich würde als Faustregel empfehlen, Destruktoren von Basisklassen standardmäßig non-virtual aber protected zu machen (per = default. Damit verhinderst du, dass versehentlich wo ein delete über einen Basepointer durchgeführt werden kann. Nur bei Klassen, wo die Möglichkeit eines delete über einen Basepointer tatsächlich gegeben sein muss (meiner Erfahrung nach eher selten), würde ich einen public virtual Desktruktor definieren. Und es reicht, des Destruktor dann in der Basisklasse virtual zu deklarieren, das macht in der Tat autom. den Destruktor aller abgeleiteten Klassen virtual.
C-/C++-Quelltext |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// nicht abstract, nichts virtual class Master { protected: // attrs // wird von anderen Klasse geerbt, dtor deklarieren ~Master() = default; public: Master(); // Master(...); void dostuff(); }; class Foo : public Master { public: Foo(); // kein dtor override notwendig }; |
C-/C++-Quelltext |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// abstract class BasePrinter { // muss virtual sein bei abstract //protected: //~BasePrinter(); // theoretisch könnte man doch alles protected machen ? public: BasePrinter(); virtual ~BasePrinter(); virtual std::string print(std::ostream& out) = 0; }; class IntPrinter : public BasePrinter { public: IntPrinter(); // kein dtor override notwendig ? // warning: 'IntWriter' has virtual functions but non-virtual destructor [-Wnon-virtual-dtor] // virtual dtor in public, nicht non-virtual dtor in protected virtual ~IntPrinter() = default; // override virtual methode std::string print(std::ostream& out) override; }; |
C-/C++-Quelltext |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// std::runtime_error hat virtual dtor class FooBaseException : public std::runtime_error { protected: // info error stuff int _errc; // warning: 'FooBaseException' has no out-of-line virtual method // definitions; its vtable will be emitted in every translation unit // [-Wweak-vtables] // dtor auslagern nach .cpp ~FooBaseException(); public: FooBaseException(int errc, std::string msg) : std::runtime_error(msg) , _errc(errc) {} int getErrc() const { return this->_errc; } }; // warning: 'FooException' has no out-of-line virtual method // definitions; its vtable will be emitted in every translation unit // [-Wweak-vtables] class FooException : public FooBaseException { public: FooException(int errc, std::string msg) : FooBaseException(errc, msg) {} // dtor override um warnung zu beheben // dtor auslagern nach .cpp ~FooException() override; // other ctors, methodes }; |
Jetzt bin ich neugierig, worin diese "Sonderstellung" besteht. Wüsste von keiner. ö.ö
Solche oder ähnliche Aussagen lese ich von dir erstaunlich oft. Du verwendest nur selten Polymorphie, oder? Während das in vielen Business-Anwendungen tatsächlich nicht oft benötigt wird, ist es bei Spielen doch schon eher mal an der Tagesordnung.Nur bei Klassen, wo die Möglichkeit eines delete über einen Basepointer tatsächlich gegeben sein muss (meiner Erfahrung nach eher selten), würde ich einen public virtual Desktruktor definieren.
Ich hab noch etwas weiter rum probiert, wie es denn mit virtual Methoden aussieht:
Dort muss ich virtual dtor nehmen, wegen der Warnung: non-virtual-dtor
Zudem krieg ich noch die Warnung, weak-vtables:
Wenn es einen virtual dtor gibt und die abgeleiteten Klasse kein dtor hat:
Also noch mal Zusammengefasst:
- Bei non-virtual Basis Klasse: protected non-virtual dtor verwenden
- virtual dtor in Ausnahmefällen verwenden z.B. selber Verwalten von Objekten mit delete und BasisKlasse
- Wenn es geht dtor() = default; verwenden
- Bei (abstrakter) Basis Klasse mit virtual Methoden: virtual dtor verwenden (- Wnon-virtual-dtor Warnung)
- virtual Methoden (oder Destruktor) deklarieren und in .cpp definieren (-Wweak-vtables Warnung)
- Hat die Basis Klasse ein virtual dtor, sollte der dtor in der abgeleiteten Klasse überschreiben werden, ~dtor() override; (-Wweak-vtables Warnung)
Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von »dot« (01.04.2015, 02:36)
Community-Fossil
Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer
Hmm, kann ich leider so nicht bestätigen. Das stimmt nach meiner Erfahrung nur solange, wie man nur ein konkretes Objekt hat. Hat man aber mehrere oder gar eine Factory (die ein Objekt liefert und gleichzeitig auch die Ownership zurück überträgt), weiß man hinterher eigentlich nicht mehr was ursprünglich mal wie erzeugt wurde. So ein Fall scheint bei Spielen doch sehr üblich zu sein - Entities (NPCs/Monster/etc), AI-Typen, Ausrüstungen, etc.Den Fall, dass ich polymorphes delete benötige, habe ich relativ selten, da der Besitzer eines Objektes meistens den konkreten Typ kennt, auch wenn das Objekt an anderen Stellen polymorph verwendet wird...
Werbeanzeige