Efter double x() strå der const. Hvad betyder det? Yderligere undrer det mig at De i eksemplet her viser assignmet constructoren - den bliver added af compileren ik? Og hvis der er nogen grund til hvorfor det kan være smart at selv definere disse "ekstra" constuctorer - hvad er det så?
const efter funktions navn betyder at funktionen ikke modificerer det object den er kaldt på, og at funktionen kan kaldes på const objecter.
Hvis kompileren kan lave en assignment operator er der som regel ikke nogen grund til at lave en selv, men i nogle tilfælde er du nødt til at lave en selv. Hvis class'en indeholder en pointer bør man altid lave en assignment operator.
Problemet er at efter linien "S1 = S2;" vil både S1 og S2 pege på den samme streng, så nå man nedlægger S1 og S2 (i destructoren) vil de begge forsøge at delete det samme memory, og ingen delete'er det S2 pegede på.
En rigtig assignment operator kunne se sådan ud: StringClass &operator = (const StringClass &rhs) { delete [] Str; Str = new char [strlen(rhs.Str) + 1]; strcpy(Str, rhs.Str); return *this; }
Og så skal man også lave en copy constructor og en default constructor!
Det er mest et spørgsmål om smag og behag. Hvis man skriver "using namespace std;" er der en lang række navne man ikke kan bruge. Ved at skrive std:: foran er der ikke nogen der kan være i tvivl om at delen efter er en del af standard biblioteket. Man kan også skrive: using std::cout; Så er det kun cout det gælder for.
Der er en fejl i min << operator, det skal være: StringClass &operator = (const StringClass &rhs) { delete [] Str; Str = new char [strlen(rhs.Str) + 1]; strcpy(Str, rhs.Str); return *this; }
Ellers vil den vise hvorfor der skal være en copy constructor!
Der er glemt en vigtig detalje ved den viste assignment operator, nemlig at sikre at den er veldefineret, hvis et objekt tildeles sig selv.
Det kan virke pedantisk, og hvem vil dog nogensinde gøre noget sådant. Men når først et C++ program vokser i størrelse, og referencer til objekter bliver brugt - kan denne situation sagtens optræde... Og kan være et mareridt at debugge sig frem til.
Derfor er en bedre implementation af operator=, denne :
Hej begge to - sry jeg var lidt langsom til at få prøvet det af.. Bertel jeg har prøvet dit eksempel, men mangle lige lidt inden forståelsen er helt på plads..
Jeg har lavet det lidt anderledes - men det er vis kun et spm. om fremgangsmåde:
// Bertel der viser hvorfor man i nogel tilfaelde skal lave assignment // operatorer #include <cstring> #include <iostream> using namespace std;
int main() { StringClass S1("Hello"); StringClass S2("World");
cout << S1 << " " << S2 << endl;
S1.printAddr(); S2.printAddr();
S1 = S2;
cout << S1 << " " << S2 << endl;
S1.printAddr(); S2.printAddr();
}
Output her er så:
Hello World Adresse: 0xbffff150 Adresse: 0xbffff140 Hd Hd Adresse: 0xbffff150 Adresse: 0xbffff140
Tilsyneladende kommer S2 altså ikke til at pege på S1? Kan man ikke sige at hvis jeg selv laver en klasse - så skal jeg selv overloade assignment operatoren hvis jeg vil have en = til at funke (samt vel oxo alle andre operatorer)..?
Ok - nu begynder det at blive lidt kompliceret, så jeg vil prøve at starte med at forklare lidt om hvad C++ laver bagved scenen, når du laver en ny klasse.
Men det, at du ikke har gjort det, gør at der bliver oprettet en ny lokal instans af din StringClass - vha. den usynlige copy constructor. Den sørger så for at lave en kopi af indholdet af klassen, MEN (dette er det store MEN, mere om det senere) sørger ikke for at at lave en kopi af det der bliver peget på - den kopierer blot pegeren.
Når denne lokale kopi så bliver nedlagt - vil den hukommelse du har allokeret blive frigivet. Hvilket betyder at det oprindelige objekt nu peger på noget udefineret.
Når du så laver en tildeling, sker det samme - men i dette tilfælde peger de allerede på noget udefineret, så anden gang du skriver ud er det udefineret hvad der står. (Hd i dit eksempel).
For at summere op :
Hvis du ikke laver dem, vil C++ automatisk lave en copy constructor og en assignment operator for dig. Disse laver en primitiv kopi af indholdet af det data der er i din klasse, hvilket betyder at når man arbejder med pointere, så mister man nogen som helst mulighed for at vide hvornår man egentlig skal frigive det allokerede stykke hukommelse, som de forskellige kopier arbejder på.
Derfor :
Så snart en klasse har pointere, skal man enten
1) Selv skrive copy constructor og assignment operator, således at man har styr på at hver klasse har sit eget hukommelsesområde - eller de i det mindste kan finde ud af at dele det samme på en fornuftig måde.
F.eks. bruger en tæller til at holde styr på hvor mange steder fra det samme stykke hukommelse er kendt - således at sidste mand lukker og slukker. ( Det sidste objekt der bruger dette stykke hukommelse, frigiver det når det bliver destrueret. De andre kopier tæller blot ned, når de bliver destrueret. )
2) Sørge for at compileren ikke skriver en copy constructor og en assignment operator. Dette gøres ved at erklære dem private, og aldrig definere dem. Så vil man få en fejlbesked, hvis man laver noget kode der ellers ville bruge dem (som nu netop i forbindelse med din version af std::ostream operator<< ).
Håber at dette ikke blot gør din forvirring større - det er konceptuelt relativt hårdt C++ stof !
P. S.
Grunden til at : Tilsyneladende kommer S2 altså ikke til at pege på S1? Er at det du skriver ud, er ikke hvad str peger på, men adressen pointervariablen i klassen.. Da S1 og S2 er to forskellige klasser, er dette også to forskellige adresser. Det er indholdet på denne adresse, der har ændret sig. De du nok forsøger at skrive ud, er :
Mit tidligere svar, tog udgangspunkt i den kode du (og bertelbrander) havde lavet - men selve kernen i problemet kan forklares således. (Samme koncept, anden fremgangsmåde).
1) Hvad er copy construktor og assignment operator, og hvornår bruges de. (Har du allerede rimeligt styr på). 2) Hvad laver de versioner af copy construktor og assignment operator, som compileren automatisk laver. 3) Hvorfor dette er et problem i forbindelse med hukommelsesallokering og pointere. 4) Løsning på 3.
1) Hvad er copy construktor og assignment operator, og hvornår bruges de :
En assignment operator, er relativ simpel, den bliver brugt hver gang, et opjekt tildeles et andet. Eksempel :
S1 = S2;
Den erklæres som : StringClass& operator(const StringClass &rhs);
En copy construktor, bruges når der oprettes et nyt objekt, hvis indhold kommer fra et allerede eksisterende. Eksempler på dette :
Så i mange sammenhænge er det faktisk en god hjælp at compileren automatisk (og usynligt) laver disse funktioner for en!
3) Hvorfor dette er et problem i forbindelse med hukommelsesallokering og pointere.
Fordi hvis et objekt har en constructor, der allokerer noget hukommelse, og som frigiver denne hukommelse, når det nedlagt. Vil de versioner som copy construktor og assignment operator, som compileren lave ikke vide hvordan de skal holde styr på dette hukommelse. Det er her at StringClass kommer ind som et godt eksempel :
Men her bliver this->str overskrevet, uden at det allokerede hukommelse bliver frigivet. Samtidig vil både this->Str og rhs.Str nu pege på de samme hukommelse, som de begge vil forsøge at frigive når de bliver destrueret. At frigive de samme stykke hukommelse flere gange er klassisk kogebogs opskrift på at lave kode der går ned !
bertelbranders version af operator= (med min tilføjelse) :
Først checker man for self-assignment, hvor man så intet gør. Hvis det er to forskellige objekter, så frigiver man det stykke hukommelse som objektet tidligere brugte. Og allokerer et nyt område, som dette objekt kan pege på (og kopierer indholdet). Dette sikrer at både this->Str og rhs.Str peger på hvert sit stykke hukommelse, og at de kan frigive det uafhængigt af hinanden.
4) Løsning på 3.
Bertelbrander viste en løsning, for at forklare hvorfor man nogle gange gerne ville lave sin egen assignment operator, istedet for den compileren laver.
Alternativt kan man sikre at de aldrig bliver kaldt, som jeg forklarer i mit tidligere svar.
Problemet med min code fra "07/07-2005 20:56:31" er at der mangler et & i andet argument til << operatornen. Derved bliver objectet der skal udskrevet kopieret, og copy constructoren bliver kaldt. Men da den copy constructor som kompileren laver fordi jeg ikke lavede en, ikke virker, vil objectet være ødelagt efter udskrivning.
Problemet løses ved at tilføje en & til andet argument, derved overføres en reference til objektet, og der sker ingen kopiering. Man kan også lave en rigtig copy constructor, men der er normalt ingen grund til at kopiere et object for at udskrive det.
Well - Bertelbrander samler ikke point, det gør jeg heller ikke.. Men lad os få lukket spørgsmålet.
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.