Verbesserung an Template Klasse gegen Array Überlauf



  • das klappt so leider nicht, vorallem da die ellipse(...) nur mit pods klarkommt, dh komplexere datentypen kannst du da nicht reinpacken.

    was du machen kannst ist einen start und endzeiger für ein array annehmen

    int zahlenWerte[3]={1,2,3};
    MyArray<int,3> zahlen(zahlenWerte,zahlenWerte+3);
    

    oder vielleicht gleich so ein array erwarten:

    int zahlenWerte[3]={1,2,3};
    MyArray<int,3> zahlen(zahlenWerte);
    

    für den constructor gibt es leider nicht soviele möglichkeiten, danach stehen einem dann aber viele möglichkeiten zur verfügung.



  • Hmmmm, finde ich nicht gerade sehr schön aber was solls 😞
    Dann mache ich es eben so...(vlt. finde ich ja ne vieeeeel bessere Lösung 🕶 😃 😃 )



  • Nun endlich die komplett fertige Template - Klasse:
    (Eine Frage noch im Code)

    template<class typ,int size> class MyArray {
    
                   typ* array;
                   int aktSize;
                   void grow(int newSize);
        public:
                   MyArray();
                   MyArray(const MyArray &Array_kopie);
                   MyArray(typ* arr);
    
                   typ operator=(MyArray a);
                   typ &operator[] (unsigned int i);
                   const typ &operator[](unsigned int i) const;
    
                    ~MyArray() { delete [] array; }
    
    };
    
    template<class typ,int size> MyArray<typ,size>::MyArray()
    {
                   aktSize=size;
                   array=new typ[size];
    }
    
    template<class typ,int size> MyArray<typ,size>::MyArray(const MyArray &Array_kopie)
    {
                  array=new typ[Array_kopie.aktSize];
                  aktSize=Array_kopie.aktSize;
                  for(int i=0;i<Array_kopie.aktSize;i++)
                           array[i]=Array_kopie.array[i];                
    }
    
    template<class typ,int size> MyArray<typ,size>::MyArray(typ* arr)
    {
                   aktSize=size;
                   array=new typ[size];
    
                   for(int i=0;i<size;i++)
                   array[i]=arr[i];
    
    }
    
    template <class typ,int size> typ &MyArray<typ,size>::operator[](unsigned int i)  
    {
             if(i>aktSize) grow(i);
             return array[i];
    }
    
    template <class typ,int size> const typ &MyArray<typ,size>::operator[](unsigned int i) const  
    {
                  return array[i];   /* Was soll ich hier machen wenn nen indexüberschreitung auftritt,
                                        einfach dann den UNSINNIGEN Wert ausgeben lassen, oder??? */
    
    }
    
    template <class typ,int size> typ MyArray<typ,size>::operator=(MyArray a )
    {
             delete [] array;
    
             array=new typ[a.aktSize];
    
             for(int i=0;i<a.aktSize;i++)
                     array[i]=a.array[i];                  
    }
    
    template <class typ,int size> void MyArray<typ,size>::grow(int newSize)
    {
             typ* temparray;
             int altSize=aktSize;
    
             aktSize=newSize * 2;
    
             temparray=new typ [aktSize];
    
             for(int i=0;i<aktSize;i++)
                     temparray[i]=array[i];
    
             delete [] array;
             array=temparray;
    
    }
    


  • Freak_Coder schrieb:

    template <class typ,int size> const typ &MyArray<typ,size>::operator[](unsigned int i) const
    {
    return array[i]; /* Was soll ich hier machen wenn nen indexüberschreitung auftritt,
    einfach dann den UNSINNIGEN Wert ausgeben lassen, oder??? */

    }

    Bei ungültigem Index würde ich eine Exception vom Typ out_of_range werfen.

    Caipi



  • Der op= ist noch ein bißchen teuer im Moment. Das Argument wird by value übergeben, das heißt Du ziehst erst ne Kopie um die danach zu kopieren... und gibst dann nochmal ne Kopie zurück. Da sollte auf jeden Fall ne Referenz hin.

    Lösung: entweder auf Copy & swap umsteigen oder by reference übergeben, dann aber auf Selbszuweisung achten.



  • Lösung: entweder auf Copy & swap umsteigen oder by reference übergeben, dann aber auf Selbszuweisung achten.

    oder erst auf selbst zuweisung achten und dann copy and swap

    das sähe dann so aus:

    template <class typ,int size> typ MyArray<typ,size>::operator=(const MyArray& a )
    {
        if(this!=&a){
            MyArray temp(a);
            std::swap(array,temp.array);
            std::swap(aktSize,temp.aktsize);
        }              
    }
    


  • otze schrieb:

    oder erst auf selbst zuweisung achten und dann copy and swap

    gleich copy&swap ist besser. weil selbstzuweisungen echt so selten sind, daß man sich aus performance-sicht den test sparen sollte. und notwendig isser ja nicht bei copy&swap.



  • otze schrieb:

    template <class typ,int size> typ MyArray<typ,size>::operator=(const MyArray& a )
    {
        if(this!=&a){
            MyArray temp(a);
            std::swap(array,temp.array);
            std::swap(aktSize,temp.aktsize);
        }              
    }
    

    Wieso muss ich hier ne extra Kopie von "a" namens "temp" erstellen und kann nicht direkt swap anwenden ???



  • weil a keine kopie sondern eine referenz auf das als parameter übergebene objekt ist, darf nicht einfach swap benutzt werden, weil sonst keine wertekopie sondern ein einfacher werteaustausch stattfinden würde.

    dh wenn du a=b machen würdest, würde ohne die kopie a den wert von b haben, b aber den alten wert von a!



  • Achja stimmt ja, danke...
    Habe nur "einseitig" gedacht 😃



  • So jetzt endlich(hoffentlich) der komplette/perfekte 😉 Quelltext:
    Vielleicht braucht ihn ja mal irgendjemand :p 😛

    template<class typ,int size> class MyArray {
    
                   typ* array;
                   int aktSize;
                   void grow(int newSize);
        public:
                   MyArray();
                   MyArray(const MyArray &Array_kopie);
                   MyArray(typ* arr);
    
                   typ operator=(const MyArray& a);
                   typ &operator[] (unsigned int i);
                   const typ &operator[](unsigned int i) const;
    
                    ~MyArray() { delete [] array; }
    
    };
    
    template<class typ,int size> MyArray<typ,size>::MyArray()
    {
                   aktSize=size;
                   array=new typ[size];
    }
    
    template<class typ,int size> MyArray<typ,size>::MyArray(const MyArray &Array_kopie)
    {
                  array=new typ[Array_kopie.aktSize];
                  aktSize=Array_kopie.aktSize;
                  for(int i=0;i<Array_kopie.aktSize;i++)
                           array[i]=Array_kopie.array[i];                
    }
    
    template<class typ,int size> MyArray<typ,size>::MyArray(typ* arr)
    {
                   aktSize=size;
                   array=new typ[size];
    
                   for(int i=0;i<size;i++)
                   array[i]=arr[i];
    
    }
    
    template <class typ,int size> typ &MyArray<typ,size>::operator[](const unsigned int i)  
    {
             if(i>aktSize) grow(i);
             return array[i];
    }
    
    template <class typ,int size> const typ &MyArray<typ,size>::operator[](unsigned int i) const  
    {
                  if(i<aktSize) 
                                return array[i];
                  else
                                throw out_of_range("Out of Range !!!");   
    
    }
    
    template <class typ,int size> typ MyArray<typ,size>::operator=(const MyArray& a )
    {
             if(aktSize<a.aktSize) 
             { 
               delete [] array;
               array=new typ[a.aktSize];
             }
    
             MyArray temp(a);
             std::swap(array,temp.array);
             std::swap(aktSize,temp.aktSize); 
    
             return *this;     // Ist nicht zwigend erforderlich oda ?            
    }
    
    template <class typ,int size> void MyArray<typ,size>::grow(int newSize)
    {
             typ* temparray;
             int altSize=aktSize;
    
             aktSize=newSize * 2;
    
             temparray=new typ [aktSize];
    
             for(int i=0;i<altSize;i++)
                     temparray[i]=array[i];
    
             delete [] array;
             array=temparray;
    
    }
    


  • template<class typ,int size> class MyArray {
    

    warum ist size ein template-argument? kannst doch beruhigt die initial size auf 64 festegen oder als konstruktorargument nehmen. vorteil: nicht jedes array braucht eigenen code und die exe wird kleiner, das programm am ende scneller.

    MyArray(); 
                   MyArray(const MyArray &Array_kopie); 
                   MyArray(typ* arr); 
    
                   typ operator=(const MyArray& a); 
                   typ &operator[] (unsigned int i); 
                   const typ &operator[](unsigned int i) const; 
    
                    ~MyArray() { delete [] array; }
    

    besser, man macht konstruktoren, zuweisungsop und dtor zusammen.

    MyArray(); 
                   MyArray(typ* arr); 
                   MyArray(const MyArray &Array_kopie); 
                   typ operator=(const MyArray& a); 
                    ~MyArray() { delete [] array; } 
    
                   typ &operator[] (unsigned int i); 
                   const typ &operator[](unsigned int i) const;
    

    und der rückgabetyp des op= ist gelinde gesagt gewagt.

    template<class typ,int size> MyArray<typ,size>::MyArray() 
    { 
                   aktSize=size; 
                   array=new typ[size]; 
    }
    

    leider muss das umgeschrieben werden zu

    template<class typ,int size> MyArray<typ,size>::MyArray() 
    :array(new typ[size])//das hier wichtig für wenn exceptions
    ,aktSize(size)
    { 
    }
    
    template<class typ,int size> MyArray<typ,size>::MyArray(typ* arr) 
    { 
                   aktSize=size; 
                   array=new typ[size]; 
    
                   for(int i=0;i<size;i++) 
                   array[i]=arr[i]; 
    
    }
    

    naja, wollen wir nicht doch lieber begin und end übergeben? so isses ein wenig komisch.

    template <class typ,int size> const typ &MyArray<typ,size>::operator[](unsigned int i) const   
    { 
                  if(i<aktSize) 
                                return array[i]; 
                  else 
                                throw out_of_range("Out of Range !!!");   
    
    }
    

    was soll das denn? das widerstebt mir. irgendwas ist da ganz komisch.

    return *this;     // Ist nicht zwigend erforderlich oda ?
    

    doch. für

    a=b=c;
    
    typ* temparray; //keine variablen leer stehen lassen
    

    und mach dazu noch ein StaticArray, das nicht wachsen kann und nicht new benutzt.



  • volkard schrieb:

    template<class typ,int size> class MyArray {
    

    warum ist size ein template-argument? kannst doch beruhigt die initial size auf 64 festegen oder als konstruktorargument nehmen. vorteil: nicht jedes array braucht eigenen code und die exe wird kleiner, das programm am ende scneller.

    Stimmt. Hast vollkommen recht 😃
    Das dann für jedes Array nen eigener Code erzeugt wird, daran hab ich nicht gedacht... 😃

    volkard schrieb:

    MyArray(); 
                   MyArray(const MyArray &Array_kopie); 
                   MyArray(typ* arr); 
                  
                   typ operator=(const MyArray& a); 
                   typ &operator[] (unsigned int i); 
                   const typ &operator[](unsigned int i) const; 
                                  
                    ~MyArray() { delete [] array; }
    

    besser, man macht konstruktoren, zuweisungsop und dtor zusammen.

    Sind doch !!?? 😕 😕 Oder meinste alle auf einen Haufen.

    volkard schrieb:

    MyArray(); 
                   MyArray(typ* arr); 
                   MyArray(const MyArray &Array_kopie); 
                   typ operator=(const MyArray& a); 
                    ~MyArray() { delete [] array; } 
    
                   typ &operator[] (unsigned int i); 
                   const typ &operator[](unsigned int i) const;
    

    und der rückgabetyp des op= ist gelinde gesagt gewagt.

    O man. Upps soll natürlich MyArray heißen...

    volkard schrieb:

    leider muss das umgeschrieben werden zu

    template<class typ,int size> MyArray<typ,size>::MyArray() 
    :array(new typ[size])//das hier wichtig für wenn exceptions
    ,aktSize(size)
    { 
    }
    

    Von dem Code verstehe ich nur Bahnhooof 😕

    volkard schrieb:

    naja, wollen wir nicht doch lieber begin und end übergeben? so isses ein wenig komisch.

    Ok OK 🕶

    volkard schrieb:

    template <class typ,int size> const typ &MyArray<typ,size>::operator[](unsigned int i) const   
    { 
                  if(i<aktSize) 
                                return array[i]; 
                  else 
                                throw out_of_range("Out of Range !!!");   
            
    }
    

    was soll das denn? das widerstebt mir. irgendwas ist da ganz komisch.

    Habe ich nen Reh umgefahren ??? Was ist daran komisch 😕

    volkard schrieb:

    typ* temparray; //keine variablen leer stehen lassen
    

    Wie wo was ???? Was ist den daran Leer 😕

    volkard schrieb:

    und mach dazu noch ein StaticArray, das nicht wachsen kann und nicht new benutzt.

    Meinst du noch ne Template-Klasse mit normalen Arrays ?



  • genau, ne template klasse mit normalen arrays. dann hat wenigsten der template parameter "size" einen sinn 😃



  • Was bringt mir dann diese Klasse. Was soll ich denn da bei einem Arrayüberlauf machen? --> Nichts einfach so lassen oder ???



  • Freak_Coder schrieb:

    Was bringt mir dann diese Klasse. Was soll ich denn da bei einem Arrayüberlauf machen? --> Nichts einfach so lassen oder ???

    assert //ziemlich gut
    oder
    __asm int 3 //lustig, doch hilfreich
    oder
    throw //pass nicht zum c++-stil

    oder ganz perfekt ein eigenes ASSERT, was erst asm int 3 macht (aber nur wenn ein debugger mitläuft) und dann throw (ne eigene exception-klasse mit __LINE__ und __FILE__ übergeben).

    ach, mach erstmal einfach assert. indexgrenzenüberschreitungen sind halt so häufige fehler und mit ner eigenen array-klasse so einfach wegzumachen, daß man eigenlich immer ne eigene klasse nehmen sollte.



  • Ok! Weiß zwar noch nicht über asserts aber kann mir ja das Wissen aneignen 😃

    Wäre mal gut wenn du mal auf Seite 4 schauen würdest da habe ich dir noch ne Menge fragen gestellt(letzer Beitrag(Die assert frage hat sich dann wohl da geklärt))...



  • Freak_Coder schrieb:

    Wäre mal gut wenn du mal auf Seite 4 schauen würdest da habe ich dir noch ne Menge fragen gestellt(letzer Beitrag...

    dachte, alles sei geklärt.

    mit leer meinte ich uninitialisiert. siehe http://www.volkard.de/Cpp/Tutorial/Grundlagen/Variablen/index.html

    und ja, ich meine eine array-klasse um ein normales array. der wunsch müßte auf seite 1 schon stehen.

    template<class typ,int size> class MyArray {
    	typ array[size];
        public: 
    	typ &operator[] (unsigned int i); 
        const typ &operator[](unsigned int i) const; 
    };
    

    und in die op[] fein assert oder irgendwas rein.



  • Wenn ich das richtig gesehen habe gibt der op= immer noch ne Kopie zurück und ist damit noch etwa doppel so langsam wie er sein müßte.



  • Habe jetzt meine Klasse wieder umgeändert, nur gibt es etwas was ich noch nicht verstehe:

    template<class typ,int size> MyArray<typ,size>::MyArray() 
    :array(new typ[size])// Mit dieser schreibweise ruft man doch ander Ctors auf oda
    ,aktSize(size)        // aber was hat array() und aktSize() da zu suchen (nichts kapisch)
    { 
    }
    

    Einer ne Erklärung vielleicht oder nen Link ???


Anmelden zum Antworten