Ich versuche mich an einer Container-Klasse



  • template<class _Ty, class _Ax = allocator<_Ty> > class Array
    : public std::vector<_Ty,_Ax> //Du willst ja von einem konkreten Vektor erben (geht ja nicht anders)
    {
    };
    

    Namen, die mit Unterstrich beginnen, sind reserviert. Lieber nicht verwenden.



  • HumeSikkins schrieb:

    Aber noch einmal, nur für den Fall, dass das Kurzzeitgedächtnis bereits geleert wurde, std::vector ist nicht als öffentliche Basisklasse gedacht und auch nicht als solche geeignet.

    Was ist daran schlecht, bzw. (weil ich dir das jetzt einfach mal glaube) welche anderen Möglichkeiten habe ich, um den operator[] zu redefinieren?
    Ich will, dass er Indexsicher ist.

    EDIT: Die abgeleitete Klasse scheint auch nicht zu funktionieren:

    Array<int> blubb(10);
    

    error C2664: 'Array<_Ty>::Array(const Array<_Ty> &)': Konvertierung des Parameters 1 von 'int' in 'const Array<_Ty> &' nicht möglich
    with
    [
    _Ty=int
    ]
    and
    [
    _Ty=int
    ]
    Ursache: Konvertierung von 'int' in 'const Array<_Ty>' nicht möglich
    with
    [
    _Ty=int
    ]



  • Optimizer schrieb:

    HumeSikkins schrieb:

    Aber noch einmal, nur für den Fall, dass das Kurzzeitgedächtnis bereits geleert wurde, std::vector ist nicht als öffentliche Basisklasse gedacht und auch nicht als solche geeignet.

    Was ist daran schlecht, bzw. (weil ich dir das jetzt einfach mal glaube) welche anderen Möglichkeiten habe ich, um den operator[] zu redefinieren?

    std::vector ist keine gute öffentliche Basisklasse, da vector keine virtuellen Methoden und insbesondere keinen virtuellen Destruktor besizt. Es ist also zum einen nicht sinnvoll möglich ein abgeleitetes Objekt über eine Basisklassenreferenz anzusprechen (der eigentliche Grund für öffentliche Vererbung) und zum anderen kannst du sogar undefiniertes Verhalten heraufbeschwören, nämlich genau dann, wenn du ein dynamisch erzeugtes Objekt über eine Basisklassenreferenz löschst.

    Leitest du öffentliche von std::vector ab, so missbrauchst du die öffentliche Vererbung als Mittel zur simplen Codewiederverwendung (des Codes der Basisklasse). Und das ist eine Sache die man tunlichst vermeiden sollte.

    Die einzige saubere Möglichkeit ist Containment.

    template <class T>
    class Array
    {
    private:
    std::vector<int> impl_;
    public:
    // Interface das du brauchst forwarded zu impl_
    };
    

    Für schreibfaule (keine gute Motivation) mag auch private-Vererbung noch ok sein.



  • Hei, noch nie probiert mit privater Vererbung zu verhindern das ein Objekt auf einen Basiszeiger landen kann.

    Wenn das im Standard definiert ist (nehme ich an , sonst würdest es nich schreiben... 🤡)) => thx 🤡



  • Knuddlbaer schrieb:

    Hei, noch nie probiert mit privater Vererbung zu verhindern das ein Objekt auf einen Basiszeiger landen kann.

    Wenn das im Standard definiert ist (nehme ich an , sonst würdest es nich schreiben... 🤡)) => thx 🤡

    du kannst trotzdem (über umweg) einen zeiger auf die basisklasse bekommen. aber als nicht-friend-nicht-klassenmember eigentlich keine gefahr 😉



  • Für schreibfaule (keine gute Motivation) mag auch private-Vererbung noch ok sein.

    Warum? Was ist denn an private-Vererbung schlechter als an einer Membervariable? 🙄

    Kommt das nicht aufs Gleiche raus?



  • Optimizer schrieb:

    welche anderen Möglichkeiten habe ich, um den operator[] zu redefinieren?

    Einfach ein assert() im operator[] einfügen?
    Es ist ja nicht verboten die Library zu ändern.

    IMHO ist dies die beste Lösung - denn von value Typen zu erben bringt oft mehr Probleme als Vorteile.



  • @davi

    Wie denn ? Mein Compiler sagt mir bei bisherigen Versuchen das eine Umwandlung zwar vorhanden ist, aber nicht darauf zugegriffen werden kann.

    @Shade

    Ändern an der Lib ist nicht verboten aber unsinnig. Es wäre nicht mehr portabel.



  • Knuddlbaer schrieb:

    Ändern an der Lib ist nicht verboten aber unsinnig. Es wäre nicht mehr portabel.

    Wieso? es geht nicht um Funktionalität sondern um ein assert.
    Da bleibt alles portabel 🙂 Denn wenn das assert fehlt, ändert sich das Programmverhalten nicht sonderlich.



  • Knuddlbaer schrieb:

    @davi
    Wie denn ? Mein Compiler sagt mir bei bisherigen Versuchen das eine Umwandlung zwar vorhanden ist, aber nicht darauf zugegriffen werden kann.

    class private_base {
    public: 
       void foo () { cout << "foo\n"; }
    };
    
    class derived : private_base {
       friend int main();
    };
    
    int main () {
       derived d;
       d.foo();
    }
    


  • Ahja, thx



  • @Knuddlbaer
    Oder die gute alte Hacker-Schule:

    class Base
    {};
    
    class Derived : private Base
    {};
    
    int main()
    {
        Derived d;
        Base& b = (Base&)d;
    }
    

    Böser C-Cast! Hält sich nicht an Access-Level. Pfui!

    Warum? Was ist denn an private-Vererbung schlechter als an einer Membervariable?

    Z.B. das man mehr Abhängigkeiten hat.



  • @Hume: Meinst du das so, wie du es geschrieben hast? Dann ist doch meine Klasse nur noch für ints. Ich will natürlich genau die selbe Flexibilität, die vector hat.

    template <class T> 
    class Array 
    { 
    private: 
    std::vector<int> impl_; 
    public: 
    // Interface das du brauchst forwarded zu impl_ 
    };
    

    @Shade: Da hab ich zuviele Skrupel, die Lib zu ändern. Ich könnte mir höchstens vorstellen, die Header zu kopieren, umzubenennen und mit (minimalen Änderungen) dann meine eigene Klasse zu haben. 😕
    Dann wäre mir aber Veerbung gleich lieber.

    Kann ich von Klassen-Templates jetzt nicht erben? Oder muss ich den Konstruktor redefinieren?



  • Was spricht denn gegen:

    template <class T> 
    class Array 
    { 
    private: 
    std::vector<T> impl_; 
    public: 
    // Interface das du brauchst forwarded zu impl_ 
    };
    


  • < Blödsinn >



  • Optimizer schrieb:

    @Hume: Meinst du das so, wie du es geschrieben hast?

    Nein. Statt int sollte da natürlich eigentlich T stehen. Mein Fehler.

    Kann ich von Klassen-Templates jetzt nicht erben?

    Natürlich kannst du grundsätzlich von Klassen-Templates erben. Es ist aber niemals eine gute Idee öffentlich von Value-Klassen zu erben. Unabhängig davon ob das nun Template-Klassen sind oder normale.

    Oder muss ich den Konstruktor redefinieren?

    Wie meinen? Konstruktoren werden grundsätzlich nicht vererbt. Auch das ist unabhängig von Template-Klassen.



  • HumeSikkins schrieb:

    Konstruktoren werden grundsätzlich nicht vererbt. Auch das ist unabhängig von Template-Klassen.

    Ähm, richtig. Das war jetzt mein Fehler. 😃

    Ich habe jetzt, wie von dir vorgeschlagen, einen std::vector gekapselt und bin eigentlich ganz glücklich mit der Lösung. 🙂

    /* Klassenname:		Array
     * Beschreibung:	Diese Klasse kapselt einen std::vector
     *					und macht Zugriff mit ungültigen
     *					Indizes unmöglich.
     */
    template <class T> class Array  
    {  
    public: 
    	// Konstruktoren und Destruktoren:
    	inline Array()
    	{
    		// Größe auf 0 setzen, damit erst eine Größe
    		// angegeben werden muss (notfalls mit resize()),
    		// bevor das Array benutzt werden kann.
    		data.resize(0);
    	}
    	inline Array(size_t size)
    	{
    		if (size < 1)
    			throw IllegalArgumentException();
    
    		resize(size);
    	}
    	inline ~Array()
    	{
    		data.resize(0);
    	}
    
    	// Auskunftmethoden:
    	inline size_t length() const
    	{
    		return data.size();
    	}
    	inline const T& operator[] (size_t index) const
    	{
    		assert(index < length()  &&  index >= 0);
    		return data[index];
    	}
    
    	// Manipulieren der Daten im Array:
    	inline T& operator[] (size_t index)
    	{
    		assert(index < length()  &&  index >= 0);
    		return data[index];
    	}
    
    	// Manipulieren des Arrays:
    	inline void resize(size_t newSize)
    	{
    		if (newSize < 1)
    			throw IllegalArgumentException();
    
    		try
    		{
    			data.resize(newSize);
    		}
    		catch(...)
    		{
    			throw IllegalArgumentException();
    		}
    	}
    
    private:  
    	std::vector<T> data;  
    };
    

    Auch mehrdimensionale Arrays sind möglich:

    const int a = 8;
    const int b = 3;
    Array< Array<Cat> > bla(a);
    for (int i = 0; i < a; ++i) 
    	bla[i].resize(b);
    

    Nur die Erstellung ist etwas unschön. 🙄

    Aber jetzt hab ich endlich eine Array-Klasse, die man wie ein gewöhnliches Array benutzt und eine Indexprüfung vornimmt (und man kann auch noch resizen). 😋
    Danke für eure Hilfe!



  • deine inlines sind redundant - denn innerhalb einer class definition ist ein inline implizit 🙂

    warum wirfst du IllegalArgumentException wenn in resize() zB ein bad_alloc fliegt?

    warum ist resize(0) nicht erlaubt?

    warum machst du im dtor resize(0) ? lass doch den dtor von vector aufräumen

    btw: ich halte es für speicherverschwendung vector für ein statisches array zu missbrauchen - nimm doch einfach boost::Array, das ist nicht der weisheit letzter schluss, aber besser als dein 'hack'.



  • Zu inline: Du hast Recht, aber ich hab mir das angewöhnt, weil ich normal alles out-of-line schreibe. Der Compiler inlined (<- Assembler Code inlining) sowieso viel besser wie ich.

    Shade Of Mine schrieb:

    warum wirfst du IllegalArgumentException wenn in resize() zB ein bad_alloc fliegt?

    warum ist resize(0) nicht erlaubt?

    Weil ich es so will. Ich weiss nicht, was std::vector alles für Exceptions wirft, aber ich weiss, dass wenn er eine wirft, dass sie am Parameter liegt. Und jetzt brauch ich nur IllegalArgumentException auffangen in 300 Zeilen Code und weiss, aha vielleicht meine Array-Klasse. Weil ein bad_alloc fliegt AFAIK auch bei new, usw. Is halt Geschmacksache, ich denke, derjenige, der ein falsches Argument übergibt, soll wissen, dass es falsch war und nicht, welches Byte wo nicht allokiert werden konnte, aus welchen Gründen auch immer.

    Und es soll auch keine Arrays ohne Elemente geben. Da du den Standard liebst, müsstest du das begrüßen. 😉

    Shade Of Mine schrieb:

    warum machst du im dtor resize(0) ? lass doch den dtor von vector aufräumen

    Ist wahrscheinlich unnötig, hast Recht.

    Shade Of Mine schrieb:

    btw: ich halte es für speicherverschwendung vector für ein statisches array zu missbrauchen - nimm doch einfach boost::Array, das ist nicht der weisheit letzter schluss, aber besser als dein 'hack'.

    Verstehe ich nicht. Das ist ein Array mit Referenzen auf andere Arrays, so wie geschachtelte Arrays tatsächlich aufgebaut sind. Und ich denke nicht, dass std::vector wesentlich mehr Speicher benötigt als boost::array, welches übrigens auch keinen mich zufriedenstellenden operator[] hat.
    (Und Speicher ist doch sicher das wenigste Problem 😋 ).



  • Und es soll auch keine Arrays ohne Elemente geben. Da du den Standard liebst, müsstest du das begrüßen.

    Array<T> offeneJobs()
    {
       if(jobcount > 0)
         return jobs;
       else
         // Was gebe ich nun zurück ? Es gibt einfach noch keine Jobs!
    
    }
    

Anmelden zum Antworten