[ karas @ 17.10.2004. 18:06 ] @
Ako je u konstruktoru bachen izuzetak, da li je dotichni objekat delimichno stvoren ili nije uopshte? Ako je zauzeta memorija u konstruktoru, kako je osloboditi?
[ Dragi Tata @ 17.10.2004. 19:09 ] @
Objekat nije konstruisan dok se konstruktor potpuno ne izvrši. Jedino je problem ako uradiš ovako nešto:

Code:

Klasa::Klasa(bool crkni) {
  m_niz = new char[50];
  if (crkni)
    throw Izuzetak;  
}


U tom slučaju imaš curenje memorije jer si zauzeo niz od 50 bajtova a nisi ga oslobodio. Rešenja mogu da budu:

1) (bolje) da m_niz deklarišeš ne kao pointer na char,već kao std::vector<char> i onda nema brige za new-delete.
2) (lošije) da počistiš memoriju pre nego što baciš izuzetak:

Code:

Klasa::Klasa(bool crkni) {
  m_niz = new char[50];
  if (crkni) {
    delete [] m_niz;
    throw Izuzetak;
    }  
}

[ Reljam @ 17.10.2004. 19:17 ] @
Ili jos bolje da ne koristis izuzetke u C++u jer ne rade bas uvek kako treba, kao sto je DT upravo pokazao.
[ Dragi Tata @ 17.10.2004. 19:58 ] @
Recimo da je ovo pitanje (koristiti ili ne koristiti izuzetke) dosta sporno. Koliko vidim, manje-više svi C++ magovi (izbegavam izraz "gurui", hehehe): Stroustrup, Sutter, Meyers... smatraju da ih treba koristiti, a jedan od najčešćih argumenata im je upravo ova situacija. Kako ćeš da prijaviš grešku u slučaju da konstruktor nije u stanju da konstruiše objekat? Sa druge strane, pisati exception-safe kod nije baš dečja igra i mnogi su se opekli na tom terenu. Nisam ni sam siguran, da budem iskren.
[ Reljam @ 17.10.2004. 20:18 ] @
Ti magovi su tu da bi izmisljali nove stvari i dalje razradjivali postojace - to je isti set ljudi koji je izmislio cin i cout. Naravno da su to inteligentni ljudi, naravno da imaju iskustva, ali to ne znaci da je svako njihovo ponudjeno resenje (recimo exceptioni) prilagodjeno problemu. Ja nazalost nemam mnogo iskustva sa C++ exceptionima, uvek su mi ovakvi problemi izletali i onda sam odlucio da ih ne koristim. Koliko sam shvatio, postoji i dubljih problema sa njima od pravilnog odvijanja steka, pogotovu kada zadjemo u DLLove (propagacija exceptiona) i drzanje lockova. Izgleda kao da bi deo resenja bio da se za sve koriste smart pointeri, da sve bude wrappovano (fajlovi, lokovi, sve), ali onda smo se toliko priblizili MC++u da mozda sve to ni nema smisla.

Ne znam. Jos nisam video kompleksniji program koji koristi exceptione 'kako treba' (video sam neke koji koriste exceptione na par mesta, cisto reda radi), ali to naravno ne znaci da tako nesto ne postoji.

A inace u konstruktoru ni ne treba alocirati memoriju, to treba ostaviti za Initialize (barem u C++u).
[ Dragi Tata @ 18.10.2004. 01:07 ] @
Au, bre Relja. Vi "grafičari" koristite C++ kao C sa klasama. Razumem da se plašiš "ovreheada", ali recimo boost::scoped_array nema nikakav overhead u odnosu na gole pointere. Onda lepo uradiš nešto kao:

Code:

Klasa::Klasa(bool crkni) : m_niz(new char[50]) {
  if (crkni)
    throw Izuzetak;  
}


i nema da te boli glava.

I inače tvrdo verujem da C++ ima budućnost samo ako naučimo da ne koristimo gole pointere osim kad je to apsolutno neophodno, već da sve "zamotavamo" u pametne pointere. Skoro je bio jedan mnogo lep članak na CP-u o korišćenju smart pointera za automatsko čišćenje Win handleova. Jednostavno postaje besmisleno da se u 21-om veku gubi vreme na new-delete i open-close bugove.
[ Reljam @ 18.10.2004. 03:04 ] @
Ne radi se o overheadu, to uglavnom nije problem. Vise se radi o drzanju netrivijalnih objekata (sistemski hendlovi, lokovi, itd). Nije problem u tome sto si ti napisao, taj kod je ok, problem je kada se desi exception u delu koji ne ocekujes - recimo kada pozoves tudju komponentu. Takodje je problem sta se desava sa ovim sistemskim objektima, koji mozda nece biti oslobodjeni kako treba. Zez kod exceptiona je to sto te tera da ih koristis za sve, odnosno vrlo je tesko lokalizovati exceptione na samo deo programa. Jer ako komponenta A koristi exceptione, onda svako mesto gde se A koristi moze potencijalno da pukne, a ti onda moras da se potrudis da svi sistemski resursi na svim tim mestima budu na pravi nacin releasovani kada A baci exception.

A new-delete bagovi inace nisu cesta pojava, barem ne u ovome sto ja radim - vecina bafera je prealocirana ili se unapred tacno zna sta ide gde.
[ rivan @ 18.10.2004. 07:46 ] @
ovakve situacije (exception u konstruktoru) se resavaju funkcionalnim try-catch blokovima - primer iz msdn-a:

C::C(int ii)
try // function-try block
: i(f(ii))
{
// body of function goes in try block
}
catch (...)
{
// handles exceptions thrown from the constructor-initializer
// and from the constructor function body
printf("\nIn the catch block");
}
[ leka @ 18.10.2004. 12:14 ] @
Ne znam za vas - ja licno reeeeeeeeeeetko koristim obradu izuzetaka u C++-u, takodje retko koristim i STL (samo kad moram). Stavise, izbegavam i RTTI (osim u slucaju kad mi zaista treba).
[ leka @ 18.10.2004. 12:15 ] @
Citat:
I inače tvrdo verujem da C++ ima budućnost samo ako naučimo da ne koristimo gole pointere osim kad je to apsolutno neophodno, već da sve "zamotavamo" u pametne pointere.


Tada ce to prestati da bude C++.
[ Dragi Tata @ 18.10.2004. 13:33 ] @
@Relja: Pazi, izuzetke iz "tuđeg" koda u principu ne možeš da izbegneš nikako. Ako operator new ne može da alocira memoriju on će da baci izuzetak std::bad_alloc. Drugi primer, u slučaju da se desi access violation, Windows će da baci SEH, koji može da se uhvati sa catch(...) ili da se mapira na neki drugi izuzetak. Ako koristiš recimo DTL za pristup bazama podataka, dobićeš izuzetak kad god neka operacija sa bazom ne uspe. Jednostavno, izuzeci su legitiman deo C++a i treba znati kako da se radi sa njima, a ne "izbegavati ih".

@rivan: Ne znam u kom kontekstu je dat taj kod, ali to je školski primer kako ne treba raditi u konstruktoru. U slučaju izuzetka dobićeš loše inicijalizovan objekat a nećeš znati, pa ćeš ga koristiti kao da je lepo inicijalizovan i nastradaćeš. Pusti izuzetak da ode iz konstruktora i koristi try-catch kad kreiraš nov objekat te klase.

Destruktori su potpuno suprotan primer. Ne treba pustiti izuzetak da napusti destruktor ni po koju cenu.

@Leka: Ja sam odavno primetio da si ti u duši C programer. Da me ne shvatiš pogrešno, nema ničeg lošeg u tome i potpuno je legitimno koristiti C++ kao "bolji C", ali grešiš kada kažeš da su "sirovi" pointeri bitno obeležje C++a. Najvažnija osobina C++a je da sam biraš nivoe apstrakcije koje želiš da koristiš i da plaćaš samo za ono što zaista i koristiš. Siguran sam da u najvećem broju slučajeva nema racionalnog razloga za manuelno oslobađanje resursa, a u kodu koji koristi izuzetke je praktično nemoguće izbeći razna "curenja" (memorije i drugih resursa) ako ne koristiš "pametne omotače" umesto pointera i dugih handleova.

Da rekapituliram. Moguće je praviti ispravan kod ako:
1) Manuelno oslobađate resurse ali ne koristite izuzetke (teži način)
2) Automatski oslobađate resurse i koristite (ili ne) izuzetke (lakši način).
[ karas @ 18.10.2004. 16:24 ] @
Code:

Klasa::Klasa(bool crkni) {
  m_niz = new char[50];
  if (crkni) {
    delete [] m_niz;
    throw Izuzetak;
    }  
}


A ako je bachen izuzetak pri stvaranju petog chlana niza (npr. nema memorije) da li delete brishe samo ona chetiri koja su stvorena pre izuzetka?
[ Dragi Tata @ 18.10.2004. 16:33 ] @
Ako operator new baci izuzetak, onda je do njega da "počisti" memoriju. Uostalom, u tom slučaju tvoj delete neće ni da se izvrši jer ako new baci izuzetak, izvršavanje koda u konstruktoru se prekida na tom mestu.

Još jednom ti savetujem da batališ te new-delete kombinacije i da koristiš smart pointere. Evo jednog lepog članka na tu temu:

http://www.artima.com/cppsource/bigtwo.html

[ rivan @ 19.10.2004. 07:31 ] @
Code:

class a {
    char* an, *bn;
private:
    void Pocisti() {
        delete[] an;
        delete[] bn; }
public:
    a() 
        try: an(0), bn(0) {
            an = new char[1000];
            bn = new char[1000000]; // nesto puno sto ce da baci exception
        }
        catch(...) {

            Pocisti();
            throw 1;    // pusti dalje izuzetak
        }
    ~a() {
        Pocisti();
    }
};


@Dragi Tata
Naravno da nisam mislio da konstruktor "guta" izuzetak

U slucaju da nema catch bloka ako se pri alociranju bn javi izuzetak memorija zauzeta za an ce iscureti.
[ Dragi Tata @ 19.10.2004. 13:21 ] @
@rivan: Onaj prvi primer je "gutao" izuzetke, ili bar nije bilo očigledno da ih ne guta.

A što se tvog primera tiče (mada nevezano direktno za našu raspravu), razmisli šta će da se desi ako uradiš:

Code:

a prvi;
a drugi = prvi;


I još nešto, mislim da si hteo da kažeš samo throw a ne throw 1 u catch bloku.

A sad pogledaj ovo i sam kaži šta je bolje:

Code:

class a {
    std::vector<char> an, bn;
public:
    a() 
       {
       an.resize(1000);
       bn.resize(1000000);
       }
};


Nema da te boli glava oko čišćenja, kod je exception-safe a i copy semantika je ispravna za razliku od tvog primera.

Stvarno mi se čini da je osnovni problem sa C++om naopak način na koji su pisane mnoge knjige i na koji se uči ovaj jezik u školama. Umesto da nauče ljude prvo da koriste standardnu biblioteku i druga gotova rešenja, oni krenu da gnjave početnike sa alociranjem i dealociranjem memorije pa ih ubiju u pojam.

C++ može da bude jezik visokog nivoa i nema razloga da se ne koristi na taj način kad god je to moguće.
[ rivan @ 19.10.2004. 14:55 ] @
Citat:

Code:

a prvi;
a drugi = prvi;



@Dragi Tata
Ovo je podla zamena teza ;-)
Nije mi bila namera da ikog ucim kako se pise klasa koja sadrzi dinamicki alocirane objekte (sto je razlog sto nisam napisao operator = ni kopi konstruktor), nego da ilustrujem kako je moguce ispravno uraditi citavu konstrukciju objekta u konstruktoru i oporaviti se od eventualne greske bez curenja memorije.

Sto se tice throw istina je da sam hteo to da kazem ali u trenutku nisam mogao da se setim da li c++ ima samo throw bez argumenata za prosledjivanje izuzetka dalje (moja greska) - a ni throw 1; ne menja poentu

Sto se tice toga sta je bolje - ne postoji resenje koje je bolje u svakom slucaju - nekad ti je vazno vreme razvoja, a nekad vreme izvrsavanja...
[ Dragi Tata @ 19.10.2004. 15:28 ] @
Stvarno nisam hteo da ti "spuštam", već samo da pokažem da upotrebom standardnih kontejnera i pametnih pointera ubijemo više muva jednim udarcem: dobijemo exception-safe kod i ispravnu copy semantiku.

Citat:
rivan: Sto se tice toga sta je bolje - ne postoji resenje koje je bolje u svakom slucaju - nekad ti je vazno vreme razvoja, a nekad vreme izvrsavanja...


Slažem se da ne postoje "univerzalno bolja" rešenja, ali ako su ti performanse izgovor da uopšte ne koristiš standardnu biblioteku, onda praviš veliku grešku koja se zove "Premature optimization". Otkud znaš da je rešenje sa golim pointerima brže nego sa vektorom? Ako je brže, koliko je brže? Ako je zaista toliko brže da ugrožava performanse tvoje aplikacije (a to si utvrdio merenjem a ne nagađanjem), onda možeš da uradiš npr nešto ovako:

Code:

class a : boost::noncopyable {
    boost::scoped_array<char> an, bn;
public:
    a() : an(new char[1000]), bn(new char[1000000]) 
        {}
};


Ovo je svaki iole normalan kompajler u stanju da izoptimizuje tako da nema nikakvih penala u performansama. Bukvalno 0.
[ karas @ 19.10.2004. 17:16 ] @
Hvala na chlanku, razjasnio je neke stvari.
Nekako imam utisak (mozzda i pogreshan) da su auto_ptr, vector i slichne olakshice uvek usput pomenute, kao da se "prave" stvari moraju raditi preko golih pokazivacha.
Da li su funkcije iz C biblioteke (<cstdio> i dr.) chiste C funkcije ili mozze neka od njih da baci izuzetak? Da li je za svaku funkciju iz C++ biblioteke jasno deklarisano da baca izuzetak? Da se ne desi da program puca zbog bachenog izuzetka za koji nisam ni znao da postoji.
[ Dragi Tata @ 19.10.2004. 17:33 ] @
C funkcije ne bacaju izuzetke. Što se ostalog tiče, sve zavisi od kvaliteta dokumentacije koju imaš. U principu, svaka funkcija koja koristi new za alociranje memorije (kao što je vector::resize) može da baci izuzetak.

Ako nameravaš da koristiš auto_ptr, obrati pažnju da ima vrlo čudnu copy semantiku. Mislim da je bezbednije koristiti Boost smart pointere ako ne znaš tačno kako auto_ptr radi. Pogledaj recimo ovaj članak:

http://www.gotw.ca/publications/using_auto_ptr_effectively.htm
[ Dragi Tata @ 19.10.2004. 17:38 ] @
Evo, naleteh na jedan lep člančić o dilemama "izuzeci ili ne?"

http://www.dslextreme.com/users/fabrizioo/rant/en/except.html
[ rivan @ 20.10.2004. 09:31 ] @
@Dragi Tata
Citat:
upotrebom standardnih kontejnera i pametnih pointera ubijemo više muva jednim udarcem: dobijemo exception-safe kod i ispravnu copy semantiku.

Kad to kazes impliciras da resenjem na gornji nacin neces imati ni exception-safe kod (a poenta je bila da gornji kod jeste exception-safe), ni ispravnu copy semantiku, sto jednostavno nije tacno

Citat:

Slažem se da ne postoje "univerzalno bolja" rešenja, ali ako su ti performanse izgovor da uopšte ne koristiš standardnu biblioteku, onda praviš veliku grešku koja se zove "Premature optimization". Otkud znaš da je rešenje sa golim pointerima brže nego sa vektorom?

Radi se o tome da tacnu semantiku tvoje klase znas samo ti (npr. znas da pomeranje niza objekata tvoje klase trpi i memcpy, a nema potrebe za pozivanjem operatora = ili copy konstruktora (ovo ne znaci da tvoja klasa ne sadrzi pointere)) pa ti mozes da napravis dinamicki niz koji ce se bolje ponasati od bilo kog template-a koji je pisan sa namerom da funkcionise u svim slucajevima.

Citat:

možeš da uradiš npr nešto ovako:

Code:

class a : boost::noncopyable {
    boost::scoped_array<char> an, bn;
public:
    a() : an(new char[1000]), bn(new char[1000000]) 
        {}
};



Tema je bila kako pisati kod koji nece imati curenja u konstruktoru - ti dajes predlog za kod koji opet ima probleme sa curenjem.

Sto se tice auto_ptr i slicnih template-a njihova primena je prilicno ogranicena na specificne slucajeve (prosledjivanje pointera kao parametara i slicno), a nikako ne predstavljaju neko univerzalno primenljivo resenje (kako bi tim zezalicama implementirao asocijacije medju objektima gde mogu da se jave i curkularne reference i ko zna sta jos?). Druga stvar o kojoj treba voditi racuna je da li ti znas kako je TACNO implementiran neki template u STL-u - npr. vector - koliko se secam jedino sto se garantuje je da je pristup proizvoljnom elementu O(1) operacija - ali nisu svi O(1) uvek bas to - ako je vector napisan tako da cuva niz pointera na svoj sadrzaj (npr. da bi se izbeglo cesto pozivanje copy konstruktora pri produzavanju) ti imas O(1) pri pristupu elementima i potencijalno lose performanse pri maloj kolicini slobodne fizicke memorije u zavisnosti od redosleda kojim su kreirani objekti (moze da izaziva veliki broj page fault-ova iako je kolicina memorije zauzeta tim objektima mozda mala).

Poenta onog sto hocu da kazem je da ne treba izbegavati pointere (ako hoces da radis bez njih imas c# i javu), i da ne treba koristiti STL po svaku cenu (nema puno smisla izmisljati toplu vodu ako neki STL template radi TACNO ono sto ti treba i to onako kako ti hoces).

Nisam hteo da davim (iako se malo oteglo), samo da predstavim drugu stranu medalje...
[ Dragi Tata @ 20.10.2004. 13:27 ] @
Citat:
rivan:
upotrebom standardnih kontejnera i pametnih pointera ubijemo više muva jednim udarcem: dobijemo exception-safe kod i ispravnu copy semantiku.

Kad to kazes impliciras da resenjem na gornji nacin neces imati ni exception-safe kod (a poenta je bila da gornji kod jeste exception-safe), ni ispravnu copy semantiku, sto jednostavno nije tacno


Ne impliciram ništa, samo kažem da ćemo uz mnogo manje rada (i mnogo manje mogućnosti da zajebemo nešto) da dobijemo exception-safe kod i ispravnu copy semantiku uz malo ili nimalo penala u performansama ako koristimo pametne pointere na način na koji sam to pokazao.


Citat:
rivan:
Radi se o tome da tacnu semantiku tvoje klase znas samo ti (npr. znas da pomeranje niza objekata tvoje klase trpi i memcpy, a nema potrebe za pozivanjem operatora = ili copy konstruktora (ovo ne znaci da tvoja klasa ne sadrzi pointere)) pa ti mozes da napravis dinamicki niz koji ce se bolje ponasati od bilo kog template-a koji je pisan sa namerom da funkcionise u svim slucajevima.


Možeš, ne kažem da ne možeš. Kažem samo da najčešće nemaš potrebe to da radiš. Osnovni princip je da prvo napraviš program koji radi ispravno, onda meriš performanse i optimizuješ uska grla. U trenutku kad dizajniraš klase najčešće ne znaš gde su uska grla i najčešće je bolje da koristiš pouzdana i jednostavna rešenja da program lepo proradi.

Citat:
rivan:
Tema je bila kako pisati kod koji nece imati curenja u konstruktoru - ti dajes predlog za kod koji opet ima probleme sa curenjem

Može biti da mi je nešto promaklo, ali ne vidim da ovaj primer ima problema sa curenjem. Na šta misliš?

Citat:
rivan:
Sto se tice auto_ptr i slicnih template-a njihova primena je prilicno ogranicena na specificne slucajeve (prosledjivanje pointera kao parametara i slicno), a nikako ne predstavljaju neko univerzalno primenljivo resenje

Upravo obrnuto. Pametni pointeri su napravljeni i polako se uvode u standard da bi zamenili gole pointere osim u specifičnim slučajevima gde to nije moguće ili svrsishodno.

Citat:
rivan:
kako bi tim zezalicama implementirao asocijacije medju objektima gde mogu da se jave i curkularne reference i ko zna sta jos?).

Cirkularne reference se probijaju "slabim pointerima" u koje spadaju i sirovi pointeri - to najčešće nije problem. Kad jeste, može da se koristi neki nedeterministički GC kao što je Boehm-ov. Napominjem da u takvim slučajevima ni sirovi pointeri ne mogu da reše situaciju.

Citat:
rivan:
Poenta onog sto hocu da kazem je da ne treba izbegavati pointere (ako hoces da radis bez njih imas c# i javu), i da ne treba koristiti STL po svaku cenu (nema puno smisla izmisljati toplu vodu ako neki STL template radi TACNO ono sto ti treba i to onako kako ti hoces).

C# i Java imaju svoj način rešavanja problema sa curenjem memorije koji opet uvodi neke druge probleme, uglavnom taj način je dosta drugačiji od pametnih pointera u C++u i poređenje nije baš na mestu. Uglavnom, nikad nisam rekao da STL treba koristiti po svaku cenu (to bi bilo prilično glupavo sa moje strane) ali "po defaultu" najčešće treba izabrati standardne kontejnere i pametne pointere, a ne sirove - dobit u pouzdanosti programa i produktivnosti je velika a cene u performansama su ili male ili nikakve.

Citat:
rivan:
Nisam hteo da davim (iako se malo oteglo), samo da predstavim drugu stranu medalje...


Neka, neka. Lepo je "advokatisati" oko pametnih stvari za promenu ;)
[ rivan @ 20.10.2004. 14:39 ] @
Citat:

Može biti da mi je nešto promaklo, ali ne vidim da ovaj primer ima problema sa curenjem. Na šta misliš?

Code:

class a : boost::noncopyable {
    boost::scoped_array<char> an, bn;
public:
    a() : an(new char[1000]), bn(new char[1000000]) 
        {}
};

Zamisli da se desi exception prilikom alokacije bn - nece ti pomoci cinjenica da si koristio template a ne obicni pointer za an radi toga sto nece biti pozvan destruktor ni za a, a samim ti ni za objekte koje a sadrzi - u slucaju da se desi exception u konstruktoru ne poziva se destruktor...
Citat:

C# i Java imaju svoj način rešavanja problema sa curenjem memorije koji opet uvodi neke druge probleme, uglavnom taj način je dosta drugačiji od pametnih pointera u C++u i poređenje nije baš na mestu.

Ako je zabijanje glave u pesak sve dok se ne ostane skroz, ili bez 1/2 memorije, ako tako bese rade ti gc-ovi sa obilazenjem i kopiranjem, neko resavanje problema onda je definitivno bolje sto niko nije pokusao da u c++ "resi" problem na isti nacin

Mislim da smo na kraju prilicno razjasnili stvari...
[ Dragi Tata @ 20.10.2004. 14:54 ] @
Citat:
rivan
Zamisli da se desi exception prilikom alokacije bn - nece ti pomoci cinjenica da si koristio template a ne obicni pointer za an radi toga sto nece biti pozvan destruktor ni za a, a samim ti ni za objekte koje a sadrzi - u slucaju da se desi exception u konstruktoru ne poziva se destruktor...


Grešiš; a će da se "počisti". Ako ne veruješ, probaj ovo:

Code:

#include <memory>
#include <iostream>
using namespace std;

class C {
        int broj_;
public:
    C(bool baci, int broj):broj_(broj) 
        {
        if (baci)
            throw 1;
        cout << "Inicijalizovan " << broj_ << "\n";
        }
    ~C()
        {
        cout << "Skemban " << broj_ << "\n";
        }
    };

class D {
    auto_ptr<C> a, b;
public:
    D() : a(new C(false, 1)), b(new C(true, 2))
            {}
    };

int main (void)
    {
    try
        {
        D d;
        }
    catch (int i)
        {
        }
    }


Rezultat:

Inicijalizovan 1
Skemban 1
Press any key to continue

Citat:
rivan:
Ako je zabijanje glave u pesak sve dok se ne ostane skroz, ili bez 1/2 memorije, ako tako bese rade ti gc-ovi sa obilazenjem i kopiranjem, neko resavanje problema onda je definitivno bolje sto niko nije pokusao da u c++ "resi" problem na isti nacin


Složio bih se sa tobom, ali ne smem - Relja će da me bije ;)
[ rivan @ 20.10.2004. 15:42 ] @
Interesantno bi bilo to probati na vise kompajlera - mislim da standard to pitanje ostavlja nedefinisano...
[ Dragi Tata @ 20.10.2004. 15:48 ] @
Pogledaću u standardu kad budem stigao, a u međuvremenu evo šta kaže GCC:


% vi konstruktori.cpp
% g++ konstruktori.cpp -okonstruktori
% ./konstruktori
Inicijalizovan 1
Skemban 1
% g++ -v
Reading specs from /opt/gcc.3.3/lib/gcc-lib/i586-pc-interix3/3.3/specs
Configured with: : (reconfigured) : (reconfigured) /dev/fs/C/gnu2.intel/egcs.source//configure --verbose --prefix=/opt
/gcc.3.3 --disable-shared --with-stabs --enable-nls --with-local-prefix=/opt/gcc.3.3 --with-gnu-as --with-gnu-ld --enabl
e-targets=i586-pc-interix3 --enable-threads=posix
Thread model: posix
gcc version 3.3
%
[ rivan @ 21.10.2004. 07:26 ] @
Citat:
rivan: Interesantno bi bilo to probati na vise kompajlera - mislim da standard to pitanje ostavlja nedefinisano...

Nisam bio u pravu - standard kaze da se ti objekti unistavaju automatski - ja sam probao na gcc 2.95.4, 3.2.3, MS c++ 13.10 i svud radi ovako kako si napisao :-)

P.S.
ima jedna interesantna stvar u kodu koji smo pisali - a to je upotreba inicijalizacionih lista - ono sto mnogim ljudima promice je da je redosled izvrsavanja inicijalizacija u listi isti kao redosled definisanja atributa u definiciji klase - dakle i sledeci kod radi potpuno isto kao i originalni
Code:

class D {
    auto_ptr<C> a, b;
public:
    D() : b(new C(true, 2)), a(new C(false, 1))
            {}
    };

[ Dragi Tata @ 21.10.2004. 14:09 ] @
Citat:
rivan: ono sto mnogim ljudima promice je da je redosled izvrsavanja inicijalizacija u listi isti kao redosled definisanja atributa u definiciji klase


Da. Meyers je dao lep primer kako ovo može da "ujede":

Code:

class MyClass
{
  Foo myFoo;
  long lval;
public:
  MyClass( ) : lval(125), myFoo(lval) {}
};


Čupav ovaj C++, nema šta, hehehe. Na žalost, nisam mu našao dostojnu zamenu.
[ Dragi Tata @ 24.10.2004. 03:17 ] @
Da zašećerim ovu lepu raspravu "guruom nedelje 67"

http://www.gotw.ca/gotw/066.htm

Citat:

Moral #1: Constructor function-try-block handlers have only one purpose -- to translate an exception. (And maybe to do logging or some other side effects.) They are not useful for any other purpose.

Moral #2: Since destructors should never emit an exception, destructor function-try-blocks have no practical use at all.[6] There should never be anything for them to detect, and even if there were something to detect because of evil code, the handler is not very useful for doing anything about it because it can not suppress the exception.

Moral #3: Always perform unmanaged resource acquisition in the constructor body, never in initializer lists. In other words, either use "resource acquisition is initialization" (thereby avoiding unmanaged resources entirely) or else perform the resource acquisition in the constructor body.

For example, building on Example 2(b), say T was char and t_ was a plain old char* that was new[]'d in the initializer-list; then in the handler there would be no way to delete[] it. The fix would be to instead either wrap the dynamically allocated memory resource (e.g., change char* to string) or new[] it in the constructor body where it can be safely cleaned up using a local try-block or otherwise.

Moral #4: Always clean up unmanaged resource acquisition in local try-block handlers within the constructor or destructor body, never in constructor or destructor function-try-block handlers.

Moral #5: If a constructor has an exception specification, that exception specification must allow for the union of all possible exceptions that could be thrown by base and member subobjects. As Holmes might add, "It really must, you know." (Indeed, this is the way that the implicitly generated constructors are declared; see GotW #69.)

Moral #6: If a constructor of a member object can throw but you can get along without said member, hold it by pointer and use the pointer's nullness to remember whether you've got one or not, as usual. Use the Pimpl idiom to group such "optional" members so you only have to allocate once.

And finally, one last moral that overlaps with the above but is worth restating in its own right:

Moral #7: Prefer using "resource acquisition is initialization" to manage resources. Really, really, really. It will save you more headaches than you can probably imagine.
[ mipko @ 08.11.2004. 09:03 ] @
Kad vec pricamo o izuzecima treba pomenuti jos dve vrlo neprijatne zamke:

1. unexpected exceptions
2. exception u destructoru

Licno mislim da su exceptions dobar mehanizam za kontrolu gresaka. Ali zahteva jako dobro razumevanje.


pozdrav
Mirko