STL-Container (vector) & virtual Destructor



  • Hallo zusammen,

    mal eine Interessensfrage:
    Warum haben die STL Container kein virtuellen Destruktor?

    MS VS2013

    ~vector() _NOEXCEPT
    		{	// destroy the object
    		_Tidy();
    		}
    

    Damit entsteht doch unter Umständen ein Memory Leak, sofern sowas gebaut wird:

    class Derived : public std::vector<int> {
    public:
      Derived() : m_derived(new int) {}
      virtual ~Derived() { delete m_derived; }
    
    private:
      int* m_derived;
    };
    
    int main() {
      std::vector<int>* base = new Derived();
    
      delete base;
    
      return 0;
    }
    

    Randbemerkung: Ja, wenn die Assertion aktiv sind, dann fliegt eine beim Freigeben vom Speicher (_BLOCK_TYPE_IS_VALID).

    Vielen Dank.

    Viele Grüße,

    Jakob



  • jb2603 schrieb:

    Warum haben die STL Container kein virtuellen Destruktor?

    Antwort: Sie haben keine virtuellen Funktionen und sind nicht als Basisklasse konzipiert. Man sollte deshalb auch nie von ihnen erben.



  • Nathan schrieb:

    Sie haben keine virtuellen Funktionen ...

    Ja, das sehe ich auch.

    Nathan schrieb:

    .. sind nicht als Basisklasse konzipiert.

    Also wenn ich das nicht möchte, warum wurden dann die Konstruktoren nicht als protected/private markiert? Woran erkennst Du das diese nicht als Basisklassen konzipiert sind? Steht das im C++ Standard?

    template<class T>
    class MyVector {
    public:
      static MyVector<T> create(int size);
    
    private:
      MyVector() {}
    };
    

    Nathan schrieb:

    ... Man sollte deshalb auch nie von ihnen erben.

    Ja, ich weiß.

    Mir ist nur unklar, warum die die STL bereitstellen, dieses Konstrukt überhaupt zulassen und diese Verwendung nicht untersagen.

    Wenn das aber einfach "historisch" bedingt ist, dann hacke ich das Thema so ab ^^

    Danke Dir für Deine Antwort.



  • jb2603 schrieb:

    Also wenn ich das nicht möchte, warum wurden dann die Konstruktoren nicht als protected/private markiert?

    Was hat das mit Vererbung zu tun? Wäre ja schlimm wenn alle Klassen von denen man nicht erben soll die von dir gezeigte Struktur haben.

    jb2603 schrieb:

    Woran erkennst Du das diese nicht als Basisklassen konzipiert sind?

    Am fehlendenden virtual Destructor, wenn der Programmierer nicht geschlampt hat. Eine echte Erkennungsmöglichkeit gibt es nicht.



  • sebi707 schrieb:

    Was hat das mit Vererbung zu tun? Wäre ja schlimm wenn alle Klassen von denen man nicht erben soll die von dir gezeigte Struktur haben.

    Naja, damit wird zumindest das Erben untersagt -> Compilerfehler.

    Klar, wenn jeder "unnötige" Aufruf ([Move-]Assignment Operator) als schlimm angesehen wird, dann ist meine gezeigte Struktur zum davon laufen. Außer Dich stört was anderes daran. Die STL könnte ja wie Qt die Daten sharen und erst bei Bedarf kopieren. Aber hätte hätte Fahrradkette 😉

    Ok, danke Euch beiden für eure Antworten.



  • Vor C++11 gab es einfach kein spezifisches Sprachmittel um das erben von einer Klasse zuverbieten sondern nur diverse Workarounds mit jeweiligen Nachteilen. Heutzutage würde man wohl einfach ein "final" an die Klasse schreiben und gut. Das kann man aber aus gründen der Kompatibilitä wohlt nicht mehr nachträglich.



  • jb2603 schrieb:

    Die STL könnte ja wie Qt die Daten sharen und erst bei Bedarf kopieren.

    Nein, danke. COW hatten wir bis vor kurzem noch bei std::string in libstdc++. Vermissen wird das niemand. Was du jetzt für Vorteile darin siehst ist mir auch schleierhaft - wenn du keine echte Kopie von etwas willst, dann kopiere nicht.

    jb2603 schrieb:

    Naja, damit wird zumindest das Erben untersagt -> Compilerfehler.

    Damit wird das instanziieren von außen untersagt, nicht das Erben (insbesondere bei protected). Außerdem gibt es keinen Grund dafür, man kann von std::vector ganz prima erben. Die dynamische Allokation eines Containers ist ohnehin ein Ausnahmefall und ein Fall, bei dem man den gerne wieder über einen Basisklassenzeiger löschen möchte, erst recht. Darf man dann nicht mehr machen, ist aber wie gesagt für die Praxis ziemlich egal.



  • Die STL könnte ja wie Qt die Daten sharen und erst bei Bedarf kopieren.

    Wenn Container / Strings willst, die generisch optimiert sind und kein verhalten garantieren, dann nimm einfach nicht die stl standard container.
    Oft macht sowas soweiso nur in umgebungen mit GUI Sinn, da kannst z.b. auch wirklich die Qt Klassen, oder die anderer Frameworks nehmen.

    Außerdem gibt es keinen Grund dafür, man kann von std::vector ganz prima erben.

    Aber warum sollte man ?
    Warum sollte man irgendwen glauben machen, die eigene Klasse sei ein stl container ? Ohne gegen mindestens eine Designregel zu verstoßen ?

    Ciao ...


  • Mod

    RHBaum schrieb:

    Aber warum sollte man?

    Es gibt durchaus mehrere solche Situationen. Überzeugen mich zwar nicht, aber e.g.

    struct line : std::string {using std::string::string;};
    std::istream& operator>>(std::istream& is, line& l) {
        return std::getline(is, l);
    }
    


  • @RHBaum

    RHBaum schrieb:

    Außerdem gibt es keinen Grund dafür, man kann von std::vector ganz prima erben.

    Aber warum sollte man ?
    Warum sollte man irgendwen glauben machen, die eigene Klasse sei ein stl container ? Ohne gegen mindestens eine Designregel zu verstoßen ?

    Vor C++11 war das die einzige Möglichkeit etwas ähnliches wie alias template s zu machen.

    Vererbung kann in C++ zu mehr verwendet werden als nur um eine "is a" Beziehung abzubilden.


  • Mod

    hustbaer schrieb:

    @RHBaum

    RHBaum schrieb:

    Außerdem gibt es keinen Grund dafür, man kann von std::vector ganz prima erben.

    Aber warum sollte man ?
    Warum sollte man irgendwen glauben machen, die eigene Klasse sei ein stl container ? Ohne gegen mindestens eine Designregel zu verstoßen ?

    Vor C++11 war das die einzige Möglichkeit etwas ähnliches wie alias template s zu machen.

    …abgesehen von der Tatsache, dass wir vor C++11 keine vererbten Konstruktoren hatten oder welche durch perfect forwarding irgendwie simulieren konnten. Das wäre also ein verdammt nutzloses "alias template". :p



  • GiraffeTurboSetzen schrieb:

    jb2603 schrieb:

    Naja, damit wird zumindest das Erben untersagt -> Compilerfehler.

    Damit wird das instanziieren von außen untersagt, nicht das Erben (insbesondere bei protected). [...]

    Also das kompiliert bei mir nicht. Bei Dir schon oder war "private" bei Deiner Aussage exkludiert?
    (Das es mit protect geht, klar.)

    class Base {
    private:
      Base() {}
    };
    
    class Derived : public Base {
    public:
      Derived() {}
    };
    

    MS VS2013:

    1>tempapp.cpp(22): error C2248: "Base::Base": Kein Zugriff auf private Member, dessen Deklaration in der Base-Klasse erfolgte.
    1>          c:\users\jb\documents\visual studio 2013\projects\tempapp\tempapp\tempapp.cpp(17): Siehe Deklaration von 'Base::Base'
    1>          c:\users\jb\documents\visual studio 2013\projects\tempapp\tempapp\tempapp.cpp(15): Siehe Deklaration von 'Base'
    


  • Arcoth schrieb:

    …abgesehen von der Tatsache, dass wir vor C++11 keine vererbten Konstruktoren hatten oder welche durch perfect forwarding irgendwie simulieren konnten. Das wäre also ein verdammt nutzloses "alias template". :p

    Naja, gibt Fälle wo das egal ist, weil man nur den Default-Ctor braucht oder die Ctor-Signaturen alle kennt. :p

    MPL Monster kann man mit Ableiten auch schön bändigen.
    Dabei wird man zwar nicht von Containern ableiten, aber ist halt ein weiteres Beispiel für "ableiten wo es nix mit 'is a' zu tun hat".
    Bzw. nicht nur MPL sondern allgemein diverse Monsterverschachtelungen.

    Gleich wie man bei normaler Programmierung benannte Variablen für Zwischenergebnisse verwendet um Dinge übersichtlicher zu machen kann man bei Template-MP benannte Klassen verwenden, deren "Wert" man über Ableitung "definiert".



  • [quote="hustbaer"]

    Arcoth schrieb:

    …abgesehen von der Tatsache, dass wir vor C++11 keine vererbten Konstruktoren hatten oder welche durch perfect forwarding irgendwie simulieren konnten. Das wäre also ein verdammt nutzloses "alias template". :p

    Naja, gibt Fälle wo das egal ist, weil man nur den Default-Ctor braucht oder die Ctor-Signaturen alle kennt. :p /quote]
    Ups, hab den Beitrag übersehen danke Dir.

    Aber jetzt nochmal, stimmt die Aussage tatsächlich?
    Wenn ich "x86 gcc 4.4.7 mit -std=C++98" verwende, bekomme ich auch ein Compilerfehler. Nach der Aussage müsste das doch schön durch kompilieren - oder nicht?
    Siehe: http://goo.gl/JkhLj3

    Ich hab leider kein MS VS 2008 zur Verfügung um das zu testen.



  • jb2603 schrieb:

    GiraffeTurboSetzen schrieb:

    jb2603 schrieb:

    Naja, damit wird zumindest das Erben untersagt -> Compilerfehler.

    Damit wird das instanziieren von außen untersagt, nicht das Erben (insbesondere bei protected). [...]

    Also das kompiliert bei mir nicht. Bei Dir schon oder war "private" bei Deiner Aussage exkludiert?
    (Das es mit protect geht, klar.)

    class Base {
    private:
      Base() {}
    };
    
    class Derived : public Base {
    public:
      Derived() {}
    };
    

    MS VS2013:

    1>tempapp.cpp(22): error C2248: "Base::Base": Kein Zugriff auf private Member, dessen Deklaration in der Base-Klasse erfolgte.
    1>          c:\users\jb\documents\visual studio 2013\projects\tempapp\tempapp\tempapp.cpp(17): Siehe Deklaration von 'Base::Base'
    1>          c:\users\jb\documents\visual studio 2013\projects\tempapp\tempapp\tempapp.cpp(15): Siehe Deklaration von 'Base'
    

    Versuch mal von Base eine Instanz zu erstellen.

    Das was du da gemacht hast verwendete man vor C++11 um Objekte nicht kopierbar zu machen:
    https://ideone.com/ZpFyw5


  • Mod

    Mr.Long schrieb:

    jb2603 schrieb:

    GiraffeTurboSetzen schrieb:

    jb2603 schrieb:

    Naja, damit wird zumindest das Erben untersagt -> Compilerfehler.

    Damit wird das instanziieren von außen untersagt, nicht das Erben (insbesondere bei protected). [...]

    Also das kompiliert bei mir nicht. Bei Dir schon oder war "private" bei Deiner Aussage exkludiert?
    (Das es mit protect geht, klar.)

    class Base {
    private:
      Base() {}
    };
    
    class Derived : public Base {
    public:
      Derived() {}
    };
    

    MS VS2013:

    1>tempapp.cpp(22): error C2248: "Base::Base": Kein Zugriff auf private Member, dessen Deklaration in der Base-Klasse erfolgte.
    1>          c:\users\jb\documents\visual studio 2013\projects\tempapp\tempapp\tempapp.cpp(17): Siehe Deklaration von 'Base::Base'
    1>          c:\users\jb\documents\visual studio 2013\projects\tempapp\tempapp\tempapp.cpp(15): Siehe Deklaration von 'Base'
    

    Versuch mal von Base eine Instanz zu erstellen.

    Das was du da gemacht hast verwendete man vor C++11 um Objekte nicht kopierbar zu machen:
    https://ideone.com/ZpFyw5

    Darum hat er im Originalcode ja auch eine create-Methode. So kann man manuell Instanzen erzeugen, aber Instanzen können nicht automatisch erzeugt werden, wie es für Vererbung nötig wäre.


Log in to reply