Verbesserung an Template Klasse gegen Array Überlauf
-
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 endlichdu 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)
-
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); } }