Kopi konstruktor

1

Kopi konstruktor

offline
  • meka  Male
  • Počasni građanin
  • Pridružio: 06 Avg 2003
  • Poruke: 811
  • Gde živiš: Novi Sad / Vojvodina

Recimo da imam (potpuno beskorisnu) klasu
class String {     public:         String(const String &argString) {if (data) {delete []data;} data = argString.data;}         char *data; };

Recimo da imam kod String s = s1; // s1 vec postoji kao string
Gornji kod ne radi jer kad se prostor za klasu napravi, taj isti prostor se ne anulira (svi bitovi postave na nulu) pa samim tim data != 0, a u tom slucaju kopi konstruktor ne radi kako treba i program se ubija. Ako ne brisem prostor koji je zauzet na mestu na koje data pokazuje, neko bi mogao da uradi sledece

String s; s(s1); // s1 je negde gore inicijalizovano

Kako ja sad da ucinim da se pri kreiranju prostora za objekat klase String anuliraju bitovi (ne i oni na koje ce pokazivati data)? Mislim da je ovo dosta ozbiljan problem koji ja ne znam odakle da pocnem da resavam.



Registruj se da bi učestvovao u diskusiji. Registrovanim korisnicima se NE prikazuju reklame unutar poruka.
offline
  • Pridružio: 17 Mar 2004
  • Poruke: 293
  • Gde živiš: UK

Nemash jedan, nego dva problema :

1. Pogreshno koristish konstruktor
2. Pogreshno implementirash string klasu.

Za sada samo o prvom problemu, drugi je malo preobiman za jedan post (a tiche se kopiranja stringova, shared-resource-a, smart pointera i slichnih stvarchica):

Znam shta pokushavash sa onim copy-konstruktorom, ali je jako pogreshno, a pogreshno je kao posledica onog problema pod 2).
Zato recimo da posmatramo ovaj kod bez implikacije iz onog drugog problema.

Kada se objekat kreira, u konstruktoru, uopshte nemash potrebe da proveravash taj tvoj pointer, jer nije bitno na shta on pokazuje - neki kompajleri nuliraju memoriju, neki kompajleri ne, a i tebi je potpuno nebitno shta je u toj memoriji dok si u konstruktoru! Konstruktor je taj koji tu memoriju inicijalizuje! Posebno u tvom sluchaju copy-construktora.

Izbaci sve one provere koje imash u konstruktoru jer nemaju smisla, narochito 'delete [] data', to je veoma veoma loshe!



Za ubuduce : ako zhelish da inicijalizujesh chlanove klase PRE konstruktora koristi inicijalizaciju, recimo :
class String {     public:         String() : data(0)        { /* ovde josh neki kood, ali vec je data=0 */ }     private:         char *data; };

Ako bash bash zhelish da ti memorija bude 'obrisana', mozhe da koristish recimo placement new operator :

class CMyClass { /* ...... */}; void* ptr = malloc(sizeof(CMyClass)); memset(ptr, 0, sizeof(CMyClass)); CMyClass* pClass = new (ptr) CMyClass();

... tako ce ti memorija biti sigurno inicijalizovana na nulu.



Za kraj, nemoj ovo da primenjujesh na taj svoj problem, jer se string klasa sa deljenim pointerima ne implementira tako.



offline
  • meka  Male
  • Počasni građanin
  • Pridružio: 06 Avg 2003
  • Poruke: 811
  • Gde živiš: Novi Sad / Vojvodina

Kao sto rekoh, klasa je potpuno beskorisna u praksi, samo ilustruje moj problem. Znam sta konstruktor radi, ali sta je (u tom slucaju) sa onim pozivom s(s1)? Da li se pri tom poziva destruktor za s pre nego sto se pozove kopi konstruktor (probacu da ispisem u destruktoru kad se poziva pa da vidim, nisam sad kuci)? Mozda sam pogresno postavio pitanje (skoro sam siguran da jesam), ali glavna nedoumica mi je kako resavati probleme sa klasama koje dele prostor (u ovom slucaju mi je najjednostavnija klasa bila ona koju sam dao, pa jos jednom TOTALNO BESKORISNA JE).

offline
  • Pridružio: 17 Mar 2004
  • Poruke: 293
  • Gde živiš: UK

Citat:Znam sta konstruktor radi, ali sta je (u tom slucaju) sa onim pozivom s(s1)?
Nishta, taj poziv je ilegalan u obliku u kom si ga ti napisao.

Ispravno je String s(s1), ili String s = s1, u oba sluchaja se poziva copy konstruktor. U tvom primeru se jedino dobija greshka u kompajliranju.

Destruktor se poziva samo pri unishtavanju objekta, i nece biti pozvan u tvom primeru, narochito ne na "samog sebe" pre nego je konstruisan objekat.

Citat: glavna nedoumica mi je kako resavati probleme sa klasama koje dele prostor

RAII idiom i smart pointers.

offline
  • meka  Male
  • Počasni građanin
  • Pridružio: 06 Avg 2003
  • Poruke: 811
  • Gde živiš: Novi Sad / Vojvodina

Ne mogu da verujem da sam ispao takav papak. Samo s(s1) kompajler konta kao poziv funkcije. I ja smatram da sam programer ... pih. Odoh da kopam kanale. Smile bNasty, hvala za otvaranje ociju.

offline
  • mr_W 
  • Počasni građanin
  • Pridružio: 22 Mar 2004
  • Poruke: 835

@bNasty

samo mala ispravka
u slucaju String s(s1) se zaista poziva kopi konstruktor, i to samo taj jedan konstruktor. Nista drugo pre njega, nista drugo posle njega.

A String s = s1
je sasvim druga prica sa sasvim drugim problemima, jer se uopste ne poziva copy konstruktor, nego prvo obican konstruktor, a zatim assign operator. I to ako nemas definisan (overloaded) = operator, kompajer ce da radi obican memcpy() za jednostavne tipove podataka (int, pointer), i assign operator za objekte.

E sad, zamislite da imamo tu neku klasu String, koja u konstruktoru alocira memoriju za *data, a u destruktoru brise tu memoriju.. I da uradimo

String s = s1;

bez da String ima implementiran:

String & operator = (const String &);

Sta ce da se desi, desice se da ce prvo String objekat da se kreira defaultnim konstruktorom, znaci njegov *data ce biti recimo 0, pa ce zatim da se pozove nepostojeci operator = (String &), tj umesto njega obican memcpy(), tj data u s ce postati jednak data u s1.
Zvuci jednostavno, i mozda moze da radi, ali .. stvar ce da pukne kada dodje do destruktora, jer oba objekta sada (s i s1) imaju isti pokazivac, i oba ce pokusati da pozovu delete/ free na njemu.. a svi znamo koliko to nije dobra ideja Smile

Dakle, zato je bitno napraviti operator = za sopstvenu klasu, pa makar on bio private: i neimplementiran, jer ce nas kompajler upozoriti na gresku i spreciti nas da napravimo bug koji je kasnije tesko otkriti.

U praksi, to izgleda ovako:

class SomeClass {     public:       SomeClass() { /* defaultni konstruktor */ }       SomeClass(SomeClass &sc) { /* copy konstruktor */ }       SomeClass & operator = (SomeClass &sc) { /* copy assign */ }     private:       char *_data;   }; /*

U njemu ce defaultni konstruktor da inicijalizuje _data na 0,
copy konstruktor ce da inicijalizuje _data na kopiju onog prosledjenog argumenta, a copy assignment ce prvo da napravi kopiju prosledjenog argumenta, a zatim da oslobodi prethodni _data i stavi kopiju u _data.

Zasto sam sad spomenuo i ovo da se u operator = prvo kreira kopija, pa onda oslobadja stara vrednost ? .. Da bi se izbegla mogucnost da objekat ostane u nedefinisanom stanju..

Zamislite da je _data pointer na neki objekat, koji nije samo jednostavno char *. Npr Klasa2 *_data;

/* ovako bi bila 'naivna' implementacija: SomeClass &operator = (SomeClass &sc) { delete _data; _data = new Klasa2(sc._data); /* pod uslovom da Klasa2 ima copy konstruktor, ali to nije bitno */ }
I sad, deluje kao da radi, ali sta se desava ako konstruktor klase2 baci exception ? tada _data ima i dalje vrednost koju je imao i ranije, ali koju smo vec odavno delete-ovali.. jako lose, zar ne ?
/* pravilno */ SomeClass &operator = (SomeClass &sc) { Klasa2 *tmp = new Klasa2(sc._data); delete _data; _data = tmp; }

Ovo ce da radi ok.
Mozda ce neko da pita, ali sta sad ako delete baci exception, onda tmp ostaje da 'visi u vazduhu...' .. E pa ne, delete (i destruktori) nikada ne bacaju exception Smile

offline
  • Pridružio: 17 Mar 2004
  • Poruke: 293
  • Gde živiš: UK

Citat:A String s = s1
je sasvim druga prica sa sasvim drugim problemima, jer se uopste ne poziva copy konstruktor, nego prvo obican konstruktor, a zatim assign operator. I to ako nemas definisan (overloaded) = operator, kompajer ce da radi obican memcpy() za jednostavne tipove podataka (int, pointer), i assign operator za objekte.


Ne, nisi u pravu.

Namerno sam naveo primer "String s=s1" kao primer 'skrivenog' poziva copy-konstruktora jer chesto izaziva zabunu. Ovo je implicitna inicijalizacija objekta, i u njoj operator= ne igra nikakvu ulogu.

"String s=s1" je ISTOVETNO kao i napisati "String s(s1)" !

Poteraj kroz debugger ako i dalje sumnjash.

offline
  • meka  Male
  • Počasni građanin
  • Pridružio: 06 Avg 2003
  • Poruke: 811
  • Gde živiš: Novi Sad / Vojvodina

Ili jednostavno stavi jedan puts/cout u kopi konstruktor. U svakom slucaju, mr_W, pojednostavio sam klasu koliko sam god mogao da bi ilustrovao svoj "problem" sa kopi konstruktorom. Ne daj bože da neko pokuša da koristi ovu klasu. Smile Znam da, ako već treba da pravim share pointer, tu treba brojač, destruktor koji poziva delete samo kad je broj pokazivača na *data nula... Mene su učili da ljudi brže odgovaraju (samim tim što brže razumeju šta je napisano) ako je ogoljeno do kostiju.

Dopuna: 19 Dec 2005 12:15

A da. Zaboravih, mr_W, ipak je veoma korisno ovo što si napisao. Hvala!

offline
  • Pridružio: 17 Mar 2004
  • Poruke: 293
  • Gde živiš: UK

Citat:Znam da, ako već treba da pravim share pointer, tu treba brojač, destruktor koji poziva delete samo kad je broj pokazivača na *data nula...

To je gore pomenuti smart-pointer.

Mali savet, ako vec planirash da se bacish na to, izbegavaj auto_ptr iz stl-a. Pogledaj na netu primere razlichitih tipova smart-pointera, ili ako mozhesh josh bolje je naci knjigu "Modern C++ Design" Andrei Alexandrescu-a, u njoj su takve stvari objashnjenje do u sitna crevca. Bolju literaturu za tu problematiku necesh naci.

U sluchaju da ti je pointer koji delish vezan za neki resurs (bilo kog tipa, od stringa, pa do socket-a) pogledaj stvari vezane za "RAII idiom".

offline
  • mr_W 
  • Počasni građanin
  • Pridružio: 22 Mar 2004
  • Poruke: 835

bNasty ::Ne, nisi u pravu.

Namerno sam naveo primer "String s=s1" kao primer 'skrivenog' poziva copy-konstruktora jer chesto izaziva zabunu. Ovo je implicitna inicijalizacija objekta, i u njoj operator= ne igra nikakvu ulogu.

"String s=s1" je ISTOVETNO kao i napisati "String s(s1)" !

Poteraj kroz debugger ako i dalje sumnjash.


Bogami, u pravu si, nisam imao pojma da je to tako definisano.

Ko je trenutno na forumu
 

Ukupno su 1415 korisnika na forumu :: 62 registrovanih, 5 sakrivenih i 1348 gosta   ::   [ Administrator ] [ Supermoderator ] [ Moderator ] :: Detaljnije

Najviše korisnika na forumu ikad bilo je 3466 - dana 01 Jun 2021 17:07

Korisnici koji su trenutno na forumu:
Korisnici trenutno na forumu: 357magnum, 39mm, amaterSRB, Apok, Atomski čoban, Batinas, bojank, Boris90, Brana01, darkangel, DeerHunter, Dežurni pod palubom, Djokislav, dmdr, Dorcolac, drimer, Duh sa sekirom, Georgius, HogarStrashni, ikan, Ilija Cvorovic, Insan, kinez88, Klecaviks, kokodakalo, Kruger, Krvava Devetka, Kubovac, kunktator, Kure126-7, Litostroton, LUDI, Luka Blažević, Lukaaa, Lutvo_Redzepagic, milenko crazy north, mkukoleca, MrNo, nebkv, Nemanja.M, nemkea71, Neretva, oganj123, oldtimer, opt1, robert1979, royst33, sap, sasa87, slonic_tonic, Srle993, stegonosa, StepskiVuk, suton, Tvrtko I, vathra, virked, VJ, vukdra, yufighter, Zimbabwe, zlaya011