abstrakte klassen



  • hallo

    ich habe eine frage zu abstrakten klassen. mein codebeispiel:

    struct mother
    {
        virtual void f() = 0;
    };
    struct derived : mother
    {
        derived(int);
        virtual void f();
    };
    

    nun möchte ich einen vektor von daten machen, deren typ von mother abgeleitet wurde.

    vector<mother> container;
    container.push_back(derived(123));
    

    dies scheint aber nicht zu funktionieren.

    gibt es alternativen?



  • std::vector macht immer eine Kopie. Er hält keine Referenzen. Demnach muss es also möglich sein, ein Objekt vom Typ T (=mother) zu erzeugen. Man kann aber kein Objekt einer abstrakten Klasse erzeugen.



  • Was bedeutet, du musst hier mit Zeigern arbeiten.



  • welchen zeigertyp aus der standard bibliothek muss ich nutzen wenn mein compiler noch nicht c++11 fähig ist? auto ptr?



  • auto_ptr funktioniert nicht mit std::vector. Wenn es ohne C++11 oder Boost gehen soll, bleiben nur rohe Pointer.



  • Braunstein schrieb:

    Was bedeutet, du musst hier mit Zeigern arbeiten.

    Referenzen gehen bei Funktionsaufrufen auch, aber nicht bei vectoren oder Datenstrukturen generell.

    std::auto_ptr ist deprecated. Also std::shared_ptr oder std::unique_ptr wären schon das Non-Plus-Ultra. Eventuell hat dein Compiler was im std::tr1 Namensraum.

    Ansonsten kannst du dir selber einen kleinen Smart-Pointer schreiben.

    (Nicht empfehlenswerte wäre rohe Zeiger zu nutzen, da du hier die Ressourcenverwaltung komplett selbst übernehmen müsstest, da ist eine Smart-Pointerklasse schneller selber und besser geschrieben. Zur Not kopierst du dir Quellcode aus den Standardbibliotheken anderer Compiler.)



  • Skym0sh0 schrieb:

    (Nicht empfehlenswerte wäre rohe Zeiger zu nutzen, da du hier die Ressourcenverwaltung komplett selbst übernehmen müsstest, da ist eine Smart-Pointerklasse schneller selber und besser geschrieben. Zur Not kopierst du dir Quellcode aus den Standardbibliotheken anderer Compiler.)

    Wie schreibt man denn einfach eine Smart-Pointerklasse wenn der Compiler keine Move-Semantik kennt?



  • thomas 434 schrieb:

    welchen zeigertyp aus der standard bibliothek muss ich nutzen wenn mein compiler noch nicht c++11 fähig ist? auto ptr?

    boost::ptr_vector<mother>

    ptr_vector fühlt sich fast wie ein normaler Vektor an. Er kümmert sich um seine Elemente und löscht sie auch wieder automatisch. Allerdings speichert er die Objekte nicht direkt im Array sondern nur Zeiger darauf. Und beim Eifügen will es eben die Adressen haben, die du per new zurück bekommst.

    Im Notfall (wenn's nicht anders geht) auch:

    std::vector<boost::shared_ptr<mother> >

    Allerdings ist damit noch etwas mehr Overhead verbunden. Der compiler erlaubt es dir auch, solche Vektoren zu kopieren. Nur teilen sich dann die Vektoren die Objekte, was zu Fehlern führen könnte. Beispielsweise:

    std::vector<boost::shared_ptr<X> > v1;
    v1.push_back(boost::make_shared<X>(23));
    std::vector<boost::shared_ptr<X> > v2 = v1;
    v1[0]->setvalue(42);
    cout << v2[0]->getvalue() << endl; // 23 oder 42 ?
    

    Die extra Indirektion (-> bzw *), die man da verwenden muss, um an die Objekte ranzukommen, ist auch etwas lästig.

    TNA schrieb:

    Wie schreibt man denn einfach eine Smart-Pointerklasse wenn der Compiler keine Move-Semantik kennt?

    Das ist ja eigentlich nur ein Problem im Fall unique_ptr.


  • Mod

    thomas 434 schrieb:

    welchen zeigertyp aus der standard bibliothek muss ich nutzen wenn mein compiler noch nicht c++11 fähig ist? auto ptr?

    Kommt drauf an, was du machen möchtest. Soll denn der Container hier auch wirklich für die Objekte verantwortlich sein? Dann ist auto_ptr zwar eine richtige Wahl, aber keine gute. Der ist nicht ohne Grund deprecated. Besser wäre vielleicht, sich mal bei Boost umzugucken. Die haben auch ganze Container, die dafür gedacht sind, besitzende (nicht smarte) Pointer zu halten.

    Wenn die Objekte woanders leben, dann sind natürlich gänzlich andere Vorgehensweisen gefragt. Seien es shared_ptr (ebenfalls Boost) oder auch einfach nur rohe Pointer, die auf ein Objekt verweisen.



  • Soweit ich jetzt ergooglen konnte, ist boost::ptr_vector und auch boost::shared_ptr eine Header-Only Library, d.h. du kansnt sie nutzen ohne großes Kompilieren oder Bibliotheken einbinden.



  • krümelkacker schrieb:

    TNA schrieb:

    Wie schreibt man denn einfach eine Smart-Pointerklasse wenn der Compiler keine Move-Semantik kennt?

    Das ist ja eigentlich nur ein Problem im Fall unique_ptr.

    Dann braucht man aber einen reference counter. Das ist dann deutlich aufwendiger und hat zusätzlichen Overhead den man eigentlich nicht benötigt. Auch muss man dann bedenken, dass so ein Pointer nicht Threadsicher ist wie shared_pointer.



  • sorry, ich glaube ich stehe iwie auf dem schlauch

    gibt es denn irgendwelche probleme wenn ich

    template<class T>
    class wrapper
    {    
        T *ptr;
    public:
        wrapper() : ptr(NULL) {}
        wrapper(T *object) : ptr(object) {}
        ~wrapper() {this->clear();}
        void set(T *object) {this->clear(); ptr = object;}
        T& get() {return *ptr;}
        T const& get() const {return *ptr;}
        void clear() {if(ptr != NULL) delete ptr;}
    };
    

    mache und danach den vektor mit wrappern fülle? (abgesehen vom mehraufwand beim schreiben)



  • thomas 434 schrieb:

    sorry, ich glaube ich stehe iwie auf dem schlauch

    gibt es denn irgendwelche probleme wenn ich

    template<class T>
    class wrapper
    {    
        T *ptr;
    public:
        wrapper() : ptr(NULL) {}
        wrapper(T *object) : ptr(object) {}
        ~wrapper() {this->clear();}
        void set(T *object) {this->clear(); ptr = object;}
        T& get() {return *ptr;}
        T const& get() const {return *ptr;}
        void clear() {if(ptr != NULL) delete ptr;}
    };
    

    mache und danach den vektor mit wrappern fülle? (abgesehen vom mehraufwand beim schreiben)

    Jede Menge Probleme entstehen dabei.
    Ich sag nur: kopieren.



  • Nur, falls du es noch nicht gemerkt hast: du versuchst gerade, einen "Smart Pointer" nachzuprogrammieren.


  • Mod

    thomas 434 schrieb:

    sorry, ich glaube ich stehe iwie auf dem schlauch

    gibt es denn irgendwelche probleme wenn ich
    [...]
    mache und danach den vektor mit wrappern fülle? (abgesehen vom mehraufwand beim schreiben)

    Ja. Du hast gerade so ziemlich alles falsch gemacht, was man bei Ressourcenhaltern falsch machen kann. Regel der großen Drei nicht beachtet, esoterische Vorstellungen über Nullzeiger, komische Initialisierung.



  • was meinst du mit "esoterische Vorstellungen über Nullzeiger, komische Initialisierung."? die regel mit dem zuweisungsoperator / kopierkonstruktoer kenn ich, ist mir in der eile entgangen, genau so wie dass ich in der clear funktion den zeiger auf NULL setzen muss.


  • Mod

    thomas 434 schrieb:

    was meinst du mit "esoterische Vorstellungen über Nullzeiger, komische Initialisierung."? die regel mit dem zuweisungsoperator / kopierkonstruktoer kenn ich, ist mir in der eile entgangen, genau so wie dass ich in der clear funktion den zeiger auf NULL setzen muss.

    komische Initialisierung: Mir fehlt eben alles, was der unique_ptr der Standardbibliothek/Boost kann. Dies ist sicherlich der kleinste Kritikpunkt, da es nur Komfort ist, der sich hinzufügen lässt. Die fehlende Nichtbeachtung der großen Drei ist hingegen ein handfester technischer Fehler, wegen dem man so etwas gerade nicht selber programmiert.

    Der Kram mit den Nullzeigern zeigt, dass du ein paar Grundlagen nicht kennst oder falsch verstanden hast. Oder wahrscheinlicher: Bei jemandem abgeschrieben hast, der sich nicht auskennt. Um das genauer heraus zu finden: Erklär mal Zeilen 6 und 12.



  • ich dachte mir, wenn man einen vektor mit xy tausend einträgen macht, den speicher aber auf ein mal statt nach und nach zu allozieren möchte, so muss der wrapper auch einen zustand haben, indem er kein objekt "hält". dies ist gegeben wenn der zeiger auf NULL zeigt.

    infolge dessen muss natürlich vor dem löschen beachtet werden ob der wrapper ein objekt hütet oder ob gar kein objekt alloziert wurde, so wird vor dem löschen geprüft ob der zeiger wirklich != NULL ist. in zeile 12 ist mir wie gesagt ein fehler unterlaufen, nach dem delete sollte natürlich der ptr zwangsweise auf NULL gesetzt werden.



  • template<class T> 
    class wrapper 
    {     
        T *ptr;
    public:
        wrapper() {ptr = new T;}
        wrapper(wrapper<T> const& rhs) {ptr = new T; *ptr = rhs.get();}
        ~wrapper() {delete ptr;}
        wrapper& operator= (wrapper const& rhs) {*ptr = rhs.get(); return *this;}
        T& get() {return *ptr;} 
        T const& get() const {return *ptr;} 
    };
    

    hier eine ein bisschen bessere realisierung


  • Mod

    thomas 434 schrieb:

    template<class T> 
    class wrapper 
    {     
        T *ptr;
    public:
        wrapper() {ptr = new T;}
        wrapper(wrapper<T> const& rhs) {ptr = new T; *ptr = rhs.get();}
        ~wrapper() {delete ptr;}
        wrapper& operator= (wrapper const& rhs) {*ptr = rhs.get(); return *this;}
        T& get() {return *ptr;} 
        T const& get() const {return *ptr;} 
    };
    

    hier eine ein bisschen bessere realisierung

    Das funktioniert so aber nicht mit polymorphen Typen. Und gerade wegen diesen wird der Aufwand ja überhaupt betrieben.


Log in to reply