Verbesserung an Template Klasse gegen Array Überlauf



  • groovemaster schrieb:

    Was ist also an der Schleife besser, als ein

    aktSize = size * 3 / 2;
    

    😕

    die laufzeit.
    aber hast recht,

    aktSize = size * 2;
    

    wäre nicht schrecklich schlecht.

    ob man *3/2 oder *2 haben mag, ist geschmckssache. *2 garantiert, daß für 1e10 einfügungen nur 2e10 elementare kopiervorgänge stattfinden. und bei *3/2 schätze ich mal, daß man auf 3e10 kopiervorgänge kommt.

    deine wahrscheinlichkeitsrechnung in allen ehren, aber fast immer macht man einfach push_back, und dann ist size==aktSize+1.

    ob man auf size aufsetzt oder auf aktSize ist auch geschmckssache. ich gehe davon aus, daß ich nen buddy allocator drunter habe und daß die initial size eine zweierpotenz ist. wenn ich auf aktSize aufsetze, habe ich keinen verschnitt vom allokator aus, da immer ein recht voller buddy verwendet werden kann. die mehrkosten des einen sprungs wegen der schleife zahle ich dafür sehr gerne.
    anderenfalls hätte ich verschnitt vom allocator bis zu 50% und darauf nochmal bis zu 50% verschnitt durch das eigene array, das macht mich leicht unglücklich.



  • OK! Sorry das ich so spät schreibe. War den ganzen Tag nicht zuhause...

    Danke an ALLE für die ganzen Tips. Werde sie umsetzten.
    Bis zur const-Überladung in meinem Buch bin ich noch nicht gekommen.(Werde aber mal schnell paar Seiten vorblättern 😃 )

    @Volkard:
    Das mit dem Fehler bei der Allozierung wollte ich ja auch noch mal nachfragen, wie ich genau drauf reagieren soll. Aber das weiß ich ja nun.

    Und std und iostream habe ich nur drin, weil ich das Prog in main() direkt testen wollte 😉

    ---> Werde jetzt mal eure ganzen Tips umsetzen (nachdem ich gegessen habe 😃 ) und dann den neuen Code posten 🕶

    --------------
    EDIT 😃 😃



  • Ach, habe gerade nen Fehler endeckt im code in copynew() sollte die letzte Bedingug in der Schleif i<altSize heißen :p ---> Thx to template



  • volkard schrieb:

    typ temparray[aktSize];
    

    das ist aber nicht standard.

    Das finde ich aber jetzt auch komisch, das dürfte doch auch garnicht gehen.
    aktSize muss doch ne Konstante sein, oda???

    Dev-Cpp und Borland spucken keine Fehler aus...

    😕 😕



  • So, hier der neue Code. Bitte wegen dem exit(EXIT_FAILURE) nicht schimpfen.
    Ich weiß nämlich nicht genau was ich sonst machen soll.

    template<class typ> class MyArray {
    
                   typ* array;
                   int aktSize,altSize,t;
                   void grow(int size);
        public:
                   MyArray(int size);
                   MyArray(const MyArray &Array_kopie);
                   ~MyArray();
                   typ &operator[](const int i);
    };
    
    template<class typ> MyArray<typ>::MyArray(int size)
    {
                   aktSize=size;
                   try {
                       array=new typ[size];
                   }
                   catch (std::bad_alloc xa)
                   {
                         std::cout<<"Konnte Speicherplatz nicht reservieren";
                         exit(EXIT_FAILURE);
                   }
    }
    
    template<class typ> MyArray<typ>::MyArray(const MyArray &Array_kopie)
    {
                  try {
                       array=new typ[Array_kopie.size];
                   }
                   catch (std::bad_alloc xa)
                   {
                         std::cout<<"Konnte Speicherplatz nicht reservieren";
                         exit(EXIT_FAILURE);
                   }
    
                   for(int i=0;i<Array_kopie.size;i++)
                           array[i]=Array_kopie.array[i];                
    }
    
    template<class typ> MyArray<typ>::~MyArray()
    {
                  delete [] array;
    }
    
    template <class typ> typ &MyArray<typ>::operator[](const int i)  
    {
             std::cout<<"const\n";     
             if(i>aktSize) grow(i);
             return array[i];
    }
    
    template <class typ> void MyArray<typ>::grow(int size)
    {
             typ* temparray;
    
             altSize=aktSize;
             aktSize=size * 2;
    
             try {
                 temparray=new typ [aktSize];
             }
             catch (std::bad_alloc xa)
             {
                   std::cout<<"Konnte Speicherplatz nicht reservieren";
                   exit(EXIT_FAILURE);
             }
    
             for(int i=0;i<aktSize;i++)
                     temparray[i]=array[i];
    
             delete [] array;
             array=temparray;
    
    }
    

    volkard schrieb:

    dann noch den op[] für const überladen, effektiv c++ lesen, kopierkonstruktor und zuweisungsoperator anbieten, die zweite size als attribut wegschmeißen.

    Effektiv C++ wird definitiv mein nächstes Buch 👍
    Und wieso soll ich denn den zuweisungsoperator überladen.
    Wenn ich:

    MyArray<int> z1(10);
    MyArray<int> z2(10);
    
    for (int i=0;i<10;i++) z1[i]=i+2;
    
    z2=z1;
    

    dann wird doch ne "schöne"(?) 1 zu 1 Kopie erstellt. Oder verstehe ich wieder was falsch 😕

    Und welches zweite size wegschmeißen 😕 😕

    Und reicht das auch nicht aus:

    typ &operator[](const int i);
    

    Brauche ich den auch noch:

    typ &operator[](int i);
    

    Freak_coder schrieb:

    Bis zur const-Überladung in meinem Buch bin ich noch nicht gekommen.(Werde aber mal schnell paar Seiten vorblättern 😃 )

    ---> Sorry mein ich doch gar nicht so 😃 Habe es mit Elementfunktion die const deklariert sind verwechselt 🙄 (*schäm*)

    Ich weiß, viele viele Fragen -> Will halt C++ perfekt beherrchen 🕶
    Aber bis dahin ist es noch ein steiler weg 😉



  • Freak_Coder schrieb:

    MyArray<int> z1(10);
    MyArray<int> z2(10);
    
    for (int i=0;i<10;i++) z1[i]=i+2;
    
    z2=z1;
    

    dann wird doch ne "schöne"(?) 1 zu 1 Kopie erstellt. Oder verstehe ich wieder was falsch 😕

    es wird bitweise kopiert, dh der compiler macht dann irgendwie sowas :

    z2.array=z1.array;
    

    was ist daran schlimm?
    es werden nur die zeiger kopiert, nicht die arrays die du vorher mit new angelegt hast. z2 und z1 benutzen dann das gleiche array zum lesen und schreiben, dh veränderungen bei z1 wirken sich auch bei z2 aus. Und das willst du doch nicht, oder?

    nun zu der sache:

    try {
        array=new typ[size];
    }
    catch (std::bad_alloc xa)
    {
        std::cout<<"Konnte Speicherplatz nicht reservieren";
        exit(EXIT_FAILURE);
    }
    

    mach daraus:

    array=new typ[size];
    

    wieso du das so machen sollst?
    der benutzer dieser Klasse will selber die möglichkeit haben einzugreifen, er muss dafür sorge tragen, dass er sein programm ordnungsgemäß beendet wenns nicht anders geht. durch exit verbaust du ihm diesen weg komplett.
    Oder anders ausgedrückt: es ist nicht die aufgabe der Klasse, fehler abzufangen, der benutzer muss das tun.



  • Freak_Coder schrieb:

    Und welches zweite size wegschmeißen 😕 😕

    altSize und aktSize brauchste nicht beide als attribute. mach so wenig wie möglich zum attribut, mach so viel wie möglich zu lokalen variablen. siehe mein grow().

    Und reicht das auch nicht aus:

    typ &operator[](const int i);
    

    brauchst

    typ &operator[](int i);
    

    und

    const typ &operator[](int i)const;
    

    siehe den nächstes c++-buch. *grins*

    Ich weiß, viele viele Fragen -> Will halt C++ perfekt beherrchen 🕶

    das ist mal fein. die meisten wollen nur schnell 3d-spiele zusammenhacken. immer nett, wenn man wieder einer viel lernen will.



  • otze schrieb:

    es werden nur die zeiger kopiert, nicht die arrays die du vorher mit new angelegt hast. z2 und z1 benutzen dann das gleiche array zum lesen und schreiben, dh veränderungen bei z1 wirken sich auch bei z2 aus. Und das willst du doch nicht, oder?

    Achsoooo, stimmt ja 😃

    der benutzer dieser Klasse will selber die möglichkeit haben einzugreifen, er muss dafür sorge tragen, dass er sein programm ordnungsgemäß beendet wenns nicht anders geht. durch exit verbaust du ihm diesen weg komplett.
    Oder anders ausgedrückt: es ist nicht die aufgabe der Klasse, fehler abzufangen, der benutzer muss das tun.

    👍 👍



  • const typ &operator[](int i)const;
    

    --> Kannst mir wohl nicht auf die schnelle Erklärung wieso ich das auch so machen sollte ???

    Und bis ich das Buch mir kaufe dauerts noch was. Muss erstmal mein 2. Buch C++ Entpackt zu Ende lesen.



  • Das bewirkt, dass du die Methode auch von einem Konstanten Objekt aus aufrufen kannst (das const am Ende). Nun haste aber das Problem, dass du bei einem nichtkonstanten Objekt auch nur lesezugriff auf die Elemente bekommst. Dazu wäre dann eine Überladung gut...



  • ness schrieb:

    Das bewirkt, dass du die Methode auch von einem Konstanten Objekt aus aufrufen kannst (das const am Ende).

    Aber was bringt mir das dann ???

    const MyArray<int> zahl1(10);
    zahl1[5]=6;
    

    Da ruft ein Konstantes Objekt die Elemtfunktion auf.
    Aber der Compiler mekert, weil ich das Konstante Objekt ja nicht ändern darf...

    ->Verstehe immer noch nicht genau, was ich damit:

    const typ &operator[](unsigned int i) const;
    

    machen soll 😕



  • Freak_Coder schrieb:

    ness schrieb:

    Das bewirkt, dass du die Methode auch von einem Konstanten Objekt aus aufrufen kannst (das const am Ende).

    Aber was bringt mir das dann ???

    const MyArray<int> zahl1(10);
    zahl1[5]=6;
    

    Da ruft ein Konstantes Objekt die Elemtfunktion auf.
    Aber der Compiler mekert, weil ich das Konstante Objekt ja nicht ändern darf...

    Deswegen musst du ja auch zwei Versionen des []-Operators schreiben. 🙂
    Allgemein schreibt man zwei Versionen des []-Operators um komfortabel mit ihm zu arbeiten. D.h. man schreibt einmal eine "read-only" []-Operator-Funktion (Diese ist auch für konstante Objekte aufrufbar) _und_ eine "normale" []-Operator-Funktion. Diese ist nur für nicht-konstante Objekte aufrufbar und darf deshalb die Werte ändern.

    "normale" Version:

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

    "read-only" Version:

    template <class typ> const typ& MyArray<typ>::operator[](const unsigned int i) const
    {
           // Rest wie gehabt...
    }
    

    Nun zwei Anwendungsbeispiele:
    Du hast einen konstantes Objekt vom Typ MyArray welches du per Ctor initalisierst und die Elemente auch mit einem Default-Wert versiehst. (Das sollte dein Ctor auch noch bereitstellen).

    Nun kannst du die Werte des Arrays natürlich nicht ändern, es ist jedoch sinnvoll die Werte wenigstens auszulesen. Dazu schreibst du dann beispielsweise:

    std::cout << "\n v[0] vom Objekt v des Typs MyArray: " << v[0] << std::endl;
    

    Und hier kommt die read-only Version deiner Operator-Funktion ins Spiel. Nur, weil du diese Operator-Funktion überladen hast (bzw. eine extra-read-only Funktion geschrieben hast), kannst du obige Zeile schreiben ohne einen Compile-Error zu bekommen.

    Wenn du dagegen

    v[0] = 1;
    

    schreibst, wird die nicht read-only Funktion aufgerufen. (Natürlich darf dein Array dann nicht als const deklariert sein).

    Kurz gesagt, du schreibst eine const und eine nicht-const Version einer Operator-Funktion, damit diese auch für konstante Objekte aufrufbar ist bzw. dass auf die einzelnen Elemente eines Arrays auch _nur_ lesend zugegriffen werden kann.

    Caipi



  • Achso, thx. 👍
    Wenn der Konstruktor die Elemente schon initialisieren kann ist es ja sinnvoll.
    Verstehe es endlich 😃 😃



  • Freak_Coder schrieb:

    Achso, thx. 👍
    Wenn der Konstruktor die Elemente schon initialisieren kann ist es ja sinnvoll.
    Verstehe es endlich 😃 😃

    du meinst const?
    nein.

    es geht um

    void printAll(Array const& a){
    //also a wird von dieser funktion nicht verändert, das ist hiermit versprochen
       cout<<a[0]<<endl;//geht net
       cout<<a[1]<<endl;//geht auch net
       cout<<a[2]<<endl;//geht immer noch net
    }
    

    und da man arrays allenthalben als const-referenz übergibt (immer, wenn man kann!), ist es nötig, einen op[] anzubieten, der auch const-arrays kann.



  • volkard schrieb:

    void printAll(Array const& a){
    //also a wird von dieser funktion nicht verändert, das ist hiermit versprochen
       cout<<a[0]<<endl;//geht net
       cout<<a[1]<<endl;//geht auch net
       cout<<a[2]<<endl;//geht immer noch net
    }
    

    Wieso sollte

    cout<<a[1];
    

    nicht funktionieren. Ich verändere das Objekt doch nicht ???
    Und muss es in der Parameterliste nicht heißen:

    void printAll(const Array& a)
    

    😕

    ---EDIT-->>> Hmmm, habs gerade getestet. Funktioniert anscheinend beider sehr gut 😃



  • Freak_Coder schrieb:

    Wieso sollte

    cout<<a[1];
    

    nicht funktionieren. Ich verändere das Objekt doch nicht ???

    weiß das auch der op[] ?



  • 👍 😃 👍



  • Hallo wiedermal,
    habe noch ne Frage.

    Ich will jetzt nocheinen Konstruktor schreiben mit dem ich die Elemente inintialsieren möchte(wie Caipi es meinte).

    Meine Template Klasse sieht jetzt so aus, mit size als nicht generischen Datentyp:

    template <class typ,int size> class MyArray
    

    Nun ist das Problem das ich nicht weiß wie der Ctor aussiehen soll.

    MyArray(typ a,typ b,...);
    

    So müsste es doch irgendwie sein. Aber wie greif ich jetzt auf die "..." zu und die Parameterliste sollte doch so lang sein wie in size angegeben.

    Ich stelle mir das so vor das es am Ende so aussieht:

    MyArray<int,3> zahlen(1,5,9)
    


  • 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 🕶 😃 😃 )


Anmelden zum Antworten