von std::vector erben??



  • Hallo!

    Ich drehe hier langsam am Rad...

    Ich hab eine kleine Klasse gebaut die lange funktioniert hat und jetzt auf einmal nichtmehr. Das ist aber nicht ganz das Thema, weil ich wohl irgendwas geändert haben (muss) oder eine neue Compilerversion das anders macht?
    Wie auch immer, es geht um folgendes:

    Ich möchte eine konkretisierte vector klasse für char-arrays.

    Habe meinen Code mal in der folgenden Minimalversion nachgestellt:

    class Arr : public std::vector<char*>
    {
    	public:
    	explicit Arr() : std::vector<char*>()
    	{
    	}
    };
    
    int main()
    {
    Arr ar;
    std::vector<char *> vAr;
    return 0;
    }
    

    Ein Breakpoint nach beiden Objektinitialisierungen im Debugger mit folgenden Outputs von [Objekt].size();

    ar.size() = 3958981726
    vAr.size() = 0

    Objekt ar ist natürlich nicht wirklich funktionsfähig... aber warum?

    Steh wohl echt aufm Schlauch ...

    Danke für Hilfe!

    mfg jghj



  • jghj schrieb:

    Ich möchte eine konkretisierte vector klasse für char-arrays.

    Grundsätzlich sollte man nicht von den STL-Klassen erben. Ich weiß nicht ob das überhaupt vorgesehen ist (bei std::string ist dem definitiv nicht so; die Klasse besitzt z.B. keinen virtuellen Destruktor). Davon ganz abgesehen... Wozu?

    Eine Alternative zur Vererbung wäre natürlich noch Komposition.

    Was benötigst du was du nicht durch std::vector<char*> oder besser std::vectorstd::string gegeben hast?

    cu André



  • Ich habe deinen Code mal durch PC-Lint gejagt, und dabei kam das heraus was ich dachte:

    Warnung - base class destructor for class 'vector' is not virtual
    -- Effective C++ #14 & Eff. C++ 3rd. item 7
    Effective Note - Base class 'std::vector<char *>' is not abstract
    -- More Effective C++ #33

    cu André
    P.S: Wobei es bei mir auch keinen Fehler erzeugt (MSVC2005)



  • Ich möchte halt einfach den vector um ein paar für mich brauchbare Methoden ergänzen.

    Man sollte grundsätzlich nicht von Ihnen erben ... die Frage die sich mir stellt ist, warum die in der Klassendeklaration viel Code als protected markieren? Oder das dann eben nur für Interne Vererbungen?

    Komposition wäre natürlich eine Möglichkeit, aber dann muss ich halt alle verfügbaren mir wichtigen methoden anlegen und weiterwrappen.... Da wäre mir eine Vererbung schon etwas lieber.

    Das mit dem virtuellen Konstruktor ist natürlich ein Argument ...

    Aber warum ging das jetzt erstmal die ganze letzte Zeit? Und nur wegen dem Destruktor wird doch die size() nichts falsches zurückgeben (erstmal bin ich ja beim erstellen des Objektes)

    Komisch ist das Ganze...



  • Daran ist nichts komisch. Wenn eine Klasse keinen virtuellen Dtor hat, ist sie nicht für Vererbung vorgesehen/bestimmt. Ist doch eine ganz einfache Definition?! Eine Klasse die nicht vererbar ist, hat ja trotzdem seine Daseinsberechtigung.

    Wenn du die Vector-Klasse "erweitern" willst, kannst du doch privat "vererben" und mit using die Methoden sichtbar machen:

    class myvector : private std::vector
    {
        public:
            using size;
            using push_back;
    };
    

    Dann kann man auch size und push_back aufrufen, ohne das Vererbt wurde.



  • Artchi schrieb:

    Daran ist nichts komisch. Wenn eine Klasse keinen virtuellen Dtor hat, ist sie nicht für Vererbung vorgesehen/bestimmt. Ist doch eine ganz einfache Definition?! Eine Klasse die nicht vererbar ist, hat ja trotzdem seine Daseinsberechtigung.

    Das habe ich nicht angezweifelt.
    Ich denke du meinst meine Phrase über die protected Sections...protected kommt ja wirklich nur bei Vererbung zum tragen?
    Oder war das evtl "Gewohnheit" des Entwicklers ... oder um sich die Option für später mal offen zu halten? Aber son "virtual" vorm destructor wäre ja dann auch kein Problem gewesen 😉

    Wenn ich hier jedoch diese "virtual"-Theorie mal runterprogrammiere mit dem g++ 4.0.3 interessiert ihn das wenig.

    Ich kann nicht-virtual methoden überschreiben, ohne dass die der Basisklasse stattdessen genutzt werden. Genauso werden auch beide Destruktoren nacheinander aufgerufen, trotz nicht-virtual.

    Was das Ganze noch komischer macht, warum das dann bei vector nicht geht 😉



  • In vielen Situationen funktioniert das mit der geerbten Klasse dann ganz gut, nur bei sowas nicht:

    vector<char*> *ptr = new myvector();
    delete ptr;
    

    Hier wird dann nur der Destruktor vom std-vector aufgerufen, weil er eben nicht virtual ist.

    Hast du denn noch zusätzliche Member-Variablen in deiner von vector erbenden Klasse?



  • Artchi! dein Code funktioniert nicht!



  • Bademeister schrieb:

    Artchi! dein Code funktioniert nicht!

    Den Template-Parameter wirst du ja wohl noch selber setzen können, oder? 🙄



  • jghj schrieb:

    Ich denke du meinst meine Phrase über die protected Sections...protected kommt ja wirklich nur bei Vererbung zum tragen?
    Oder war das evtl "Gewohnheit" des Entwicklers ... oder um sich die Option für später mal offen zu halten?

    Warum der Implementierer protected-Methoden verwendet, wird sein Geheimnis bleiben. Kannst ihn ja aber gerne fragen. Implementierungsdetails sind aber irrelevant. Entscheidend ist der Standard-Vector und wie er in der ISO-Norm definiert wurde. Da steht nichts von virtuellem Dtor und es werden meines Wissens auch nur public-Methoden dort beschrieben.

    Nur weil in der GCC-Vector protected Methoden benutzt werden, muß das nicht auf den Dinkumware-Vector zu treffen. Und es gibt noch mehr Implementierungen auf diesem Planeten.

    Wenn irgendwas unter GCC oder einem anderen Compiler unerwartet funktioniert, muß es auf einem anderen Compiler nicht auch so kommen. Es wäre natürlich wünschenswert wenn sich alle Implementierungen gleich verhalten. Ist aber nicht immer.

    Ich habe jedenfalls mal in einer meiner Basis-Klassen ausversehen virtual vergessen und es hat sich in einer späteren Situation ausgewirkt... ähnlich dem Beispiel von Badestrand. Vorher ist es mir nicht aufgefallen.



  • Artchi schrieb:

    Bademeister schrieb:

    Artchi! dein Code funktioniert nicht!

    Den Template-Parameter wirst du ja wohl noch selber setzen können, oder? 🙄

    Ja aber auch dann geht es nicht.



  • error C2873: 'size' : symbol cannot be used in a using-declaration
    error C2873: 'push_back' : symbol cannot be used in a using-declaration
    


  • Ohne code natürlich sehr aussagekräftig...
    Ich hab private-Vererbung in letzter Zeit auch des öfteren gebraucht

    typedef  std::vector< std::pair<Value,Value> > SolveContainer;
    class SolveModule : public Module , private  SolveContainer {
    
            public:
            using  SolveContainer::operator[];
            using  SolveContainer::size;
            using  SolveContainer::push_back;
    ...
    

    Schreibe deine Klasse analog dazu, dann sollte es gehen.



  • Ja nur weil ichs public nich erben kann wirds im private nicht mehr Sinn machen 😉

    Mit dem virtual verhält es sich doch so, dass der Destruktor bzw. Methoden, die durch eine Subklasse neu implementiert wurde, in jedem Fall augerufen wird.

    Unabhängig von einem Upcast.

    Wurde diese Klasse jedoch nicht ge"upcasted" so macht es doch garkeinen Unterschied ob virtual oder nicht, da das Objekt die Informationen über sich selbst ja kennt.

    Beim erstellen der Subklasse wird erst der Konstruktor der Basisklasse aufgerufen um das Objekt zu erzeugen, danach der eigene. Beim Zerstören des Objektes wird erst der eigene Destruktor aufgerufen und erst dann die aller Superklassen.

    Also warum laufe ich in Probleme wenn ich nie eine Zweideutigkeit (vorallem nicht in dem kleinen Beispiel) provoziere?

    Versteht mich bitte nicht falsch, ich will diese Lösung nicht auf Teufel komm raus durchsetzen. Aber ich würde schon gerne wissen, warum ich es anders machen sollte weil sich das nicht mit der Theorie (zumindest mit meiner ;)) deckt.



  • jghj schrieb:

    Ich möchte halt einfach den vector um ein paar für mich brauchbare Methoden ergänzen.

    Gibt es einen bestimmten Grund warum du auf Teufel komm raus eine Monsterklasse basteln willst, anstelle der offensichtlichen Variante:

    // arr.hpp
    typedef std::vector<char*> arr_t;
    
    void foo( arr_t const& self );
    void bar( arr_t&       self, /*...*/ );
    // ...
    


  • Bademeister schrieb:

    error C2873: 'size' : symbol cannot be used in a using-declaration
    error C2873: 'push_back' : symbol cannot be used in a using-declaration
    

    Sorry, war halt Pseudocode (genauso wie das fehlende Template), habe den Namensraum vergessen.



  • Ich will keine Monsterklasse bauen.

    Und ich denke, dass das Objektorientierte design schon seine Daseinsberechtigung hat. Auch wenn nicht möchte ich es in diesem realisieren und meine eigenen Prozeduren als Methoden und nicht als (globale) Funktionen realisieren.



  • jghj schrieb:

    Ja nur weil ichs public nich erben kann wirds im private nicht mehr Sinn machen 😉

    Es hat keiner gesagt, das es eine Vererbung ist. Es ist eine Lösungsvorschlag, den du anscheinend nicht akzeptieren willst. Mehr können wir aber auch nicht für dich tun.

    jghj schrieb:

    Beim erstellen der Subklasse wird erst der Konstruktor der Basisklasse aufgerufen um das Objekt zu erzeugen, danach der eigene. Beim Zerstören des Objektes wird erst der eigene Destruktor aufgerufen und erst dann die aller Superklassen.

    Also warum laufe ich in Probleme wenn ich nie eine Zweideutigkeit (vorallem nicht in dem kleinen Beispiel) provoziere?

    Versteht mich bitte nicht falsch, ich will diese Lösung nicht auf Teufel komm raus durchsetzen. Aber ich würde schon gerne wissen, warum ich es anders machen sollte weil sich das nicht mit der Theorie (zumindest mit meiner ;)) deckt.

    Hem, das Problem ist, das du der Definition ausweichen und nicht akzeptieren willst. Weißt du was du willst? Du willst eine Container-Klasse spezialisieren, obwohl das mit eben DIESEN Container-Klassen nicht möglich ist. Punkt. Warum ist die Banane krumm? Du wirst keine gerade Banane finden, nur weil dir die krummen nicht fallen. Eine Banane ist per Definition krumm. Punkt!

    Wenn du von vector erbst, wird dessen Dtor nicht immer aufgerufen und somit der Vector nicht korrekt aufgeräumt. Das wirst auch du nicht ändern können. Man kann jetzt fragen, warum Vector keinen virtuellen Dtor hat. Die Frage ist berechtigt, genauso wie mit der Banane. Aber nur wegen der Frage, wird sich die Definition nicht ändern.

    Wenn du Vererbbare Container haben willst, mußt du sie selber (neu) definieren oder im Netz nach entsprechenden suchen.



  • jghj schrieb:

    Ich will keine Monsterklasse bauen.

    Und ich denke, dass das Objektorientierte design schon seine Daseinsberechtigung hat. Auch wenn nicht möchte ich es in diesem realisieren und meine eigenen Prozeduren als Methoden und nicht als (globale) Funktionen realisieren.

    Die Standard-Bibliothek ist ja auch nicht komplett OO. ⚠ C++ ist eine Multiparadigmen-Sprache, und genau das gleiche spiegelt sich in der Stdlib wieder. Viele Typen (wie die Container oder Strings) machen sich lediglich die Möglichkeit des Datenverstecken zu nutze. Das es keine Vererbung gibt, kommt durch die Templates, die das ausgleichen. Und da spielen sicherlich Performance-Gründe eine wichtige Rolle! Es ist ein Paradigma das nunmal gewählt wurde und den C++-Standard ausmacht. Wenns einem nicht gefällt, kann man eine andere Library benutzen.

    Es gibt aber auch Std-Typen (wie die Streams) die vom kompletten OOD Gebrauch machen. Da wird sogar Mehrfachvererbung (iostream) eingesetzt, da hast du sogar das Extrem-OOD! Das ist ein anderes Paradigma, das auch in der Std-Lib existiert.

    Wenn man die Std-Lib benutzt, muß man sich auf diese Paradigmen und somit Design-Entscheidungen einlassen. Und wenn man Vector nicht korrekt vererben kann, dann muß man diese Design-Entscheidung akzeptieren oder man lässt diese Klassen links liegen.



  • Artchi schrieb:

    jghj schrieb:

    Ja nur weil ichs public nich erben kann wirds im private nicht mehr Sinn machen 😉

    Es hat keiner gesagt, das es eine Vererbung ist. Es ist eine Lösungsvorschlag, den du anscheinend nicht akzeptieren willst. Mehr können wir aber auch nicht für dich tun.

    class myvector : private std::vector
    {
        public:
            using size;
            using push_back;
    };
    

    Dieser code sagt mir, DASS es eine Vererbung ist.

    Es ist ein Lösungsvorschlag, der sagt, mach das Gleiche wie ich eh wollte nur definiere mir eben alles als private. Anschließend schalte ich die gebrauchten Methoden wieder frei für den public-Zugriff.

    Dieses bringt mir ja keinen Vorteil, da es genau das gleiche Vorgehen wie ich es bereits probiert habe widerspiegelt. (Um meine Aussage zu verifizieren habe ichs zusätzlich auch noch ausprobiert; das Praxisbeispiel bestätigt meine These)

    Und wie ich vorher versucht hab zu erklären möchte ich nicht den Standard ändern und will auch nicht unbedingt diese Lösung so durchziehen, da ich über die Komposition eine angebrachte Lösung habe (die mir aber nicht ganz so elegant wie die Vererbung erscheint).

    Die Frage, die für mich interessant ist: WARUM geht das nicht? Welche Codefloskel erlaubt mir die Vererbung, ändert aber die Funktionsweise der Superklasse.

    Nicht zuletzt interessiert mich das, weil es ja für mich selbst evtl auch mal sinnvoll sein kann, eine Klasse zu erstellen von der nicht geerbt werden darf.

    Als Argument fällt für mich jedoch der virtuelle Destruktor heraus, weil der ja bei dem Objekt selbst nicht zum tragen kommt und es schon nach der Initialisierung hängt.

    Dass C++ sehr vielseitig usw. ist und auf Altlasten aufbaut ist mir bewusst, jedoch weiß ich nicht warum ich statt einer Komposition oder anderen OO Vorgehensweisen auf einmal zurück zu C soll und mir Hilfsfunktionen anlegen soll?


Log in to reply