Ich versuche mich an einer Container-Klasse



  • @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!
    
    }
    


  • Die Situation auf dem Arbeitsmarkt hat mich jetzt überzeugt. 😉
    Ich werde das also ändern.



  • Optimizer schrieb:

    Ich weiss nicht, was std::vector alles für Exceptions wirft, aber ich weiss, dass wenn er eine wirft, dass sie am Parameter liegt.

    bad_alloc fliegt, wenn kein Speicher mehr vorhanden ist... IMHO ist ein vec.resize(3); ein vollkommen legaler Aufruf - und auf einmal bekomme ich ein IllegalArgument? - ich würde mich dann nicht mehr auskennen...

    Und jetzt brauch ich nur IllegalArgumentException auffangen in 300 Zeilen Code und weiss, aha vielleicht meine Array-Klasse.

    deswegen werfe ich zB
    throw SomeException("void myNS::foo() caused critical failure due to integer overflow");

    und schon ist es klar 🙂 manchmal werfe ich auch noch __FILE__ und __LINE__ mit... aber nur dann, wenn es Debug throws sind (wovon ich aber generell abrate)

    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.

    3 ist also unter Umständen ein falsches Argument 😕

    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.

    oh doch! vector wächst ziemlich rasant. recht oft mit einem *2
    dh, wenn du einmal kurzfristig 1000 elemente brauchst, belegt das uU speicher für 2000 elemente - und den speicher bekommst du nicht weg, weil ein vector nicht schrumpft...

    aber ich glaube du willst garnicht auf mich hören 🙂 also machs so, und werde glücklich



  • Fliegt bad_alloc nur wenn kein Speicher mehr da ist? Dann kann ich ja auf diese eine Exception anders reagieren. 🕶
    Aber ich halte trotzdem nicht viel davon, wenn jemand die Klasse in einem try-Block benutzt und dann irgendeine kryptische Exception kommt und nicht mal weiss, dass die vielleicht aus dem Array kommt. Und wenn er es denn weiss, wie soll er darauf reagieren??
    Das in dem Exception Objekt noch weitere Informationen gespeichert werden, ist ja ne feine Sache, aber das ist dann wohl nicht der Fall, wenn ein bad_alloc oder was anderes ankommt von vector. Aber vom Kontext des Arrays aus kann man ziemlich genau die Ursache feststellen (ungültiger Index, was auch immer) und in das Objekt schreiben. Vom Kontext der Allokierung aus kannst du die eigentliche Ursache meistens nicht nennen.

    Nachtrag: Ich hab gerade festgestellt, dass size_t unsigned ist. Wenn jetzt jemand ein Array mit **-**500000000 Elementen anlegen will, besteht die Gefahr, dass dann tatsächlich ein Array mit **+**9376458768 Elementen angelegt wird? Soll ich lieber signed int übergeben?



  • Naja, ich stehe nicht so sonderlich auf try catch Blöcke. (Bin ich irgendwie noch zu faul für :o( *schäm*)

    Auf was ich erst recht keine Luste habe bei einem Methodenaufruf mich auf 10 verschiedene Exceptions gefasst zu machen 🤡

    Ervt Deine Exception wenigstens std::exception oder höher ?



  • NOCHT nicht (weil kannte ich nicht). Exceptions sind für mich was relativ neues, zumindest in C++. Im Moment leiten alle meine Exceptions von einer Basisklasse Exception ab.
    std::exception schau ich mir mal an, danke für den Tipp. 😃



  • Knuddlbaer schrieb:

    Naja, ich stehe nicht so sonderlich auf try catch Blöcke

    ich auch nicht. Habe momentan keine 5 try catch blöcke in knapp 8000 Zeilen - und trotzdem alles exception sicher (zumindest glaube ich das 🙂 bugs wirds aber trotzdem geben)



  • Wie sagt Sutter so schön:

    Exception safety is not about trying.



  • Shade Of Mine schrieb:

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

    Hallo Shade.

    Warum gibt es dann überhaupt das Keyword inline? Wenn man es nicht da einsetzen soll, weil redundant, wo sonst? 🙄



  • moin schrieb:

    Warum gibt es dann überhaupt das Keyword inline? Wenn man es nicht da einsetzen soll, weil redundant, wo sonst? 🙄

    inline bedeutet: link diese Funktion extern - also definiere sie nicht in jeder ÜE, auch wenn die Definition in jeder ÜE vorkommt.

    class T
    {
    public:
    void foo() {}
    void foo2();
    };

    T::foo2(){}

    T::foo() ist inline - denn alle Funktionen die in der KLasse definiert werden, sind inline.

    T::foo2() ist nicht inline



  • Jo, soweit war es mir dann klar. Aber in welchen Situationen soll man denn nun das inline Keyword benutzen, wenn man das sowieso schon implizit angeben (durch Implementation in der Klassendefinition) kann.



  • moin schrieb:

    Jo, soweit war es mir dann klar. Aber in welchen Situationen soll man denn nun das inline Keyword benutzen, wenn man das sowieso schon implizit angeben (durch Implementation in der Klassendefinition) kann.

    inline hat heutzutage nur den Sinn, dass du eine Funktion in einer Header Datei implementieren kannst.

    zB finde ich es praktisch die operatoren direkt bei der Klasse stehen zu haben

    class SomeType
    {
    ...
    };
    
    inline bool operator==(SomeType const& a, SomeType const& b)
    {
      return a.get() == b.get();
    }
    

    wenn das inline da nicht steht, ists ne ODR Verletzung.


Anmelden zum Antworten