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