[ fili @ 18.03.2002. 20:44 ] @
Cesto tokom cpp kodiranja naletim na situacije koje resim ali ostanem ubedjen da postoji elegantnije resenje - i naravno mrzi me da ga potrazim, a posle i zaboravim dok ne naletim opet na istu.
Pa evo danas sam naleteo na dve/tri, i reko' da pokrenem temu koja bi se bavila problemcicima na margini cpp-a za koje svi znamo resenja ali ne nuzno i elegantna.


1.) Kada imamo na primer dve polimorfne funkcije od kojih jedna kao parametar prima int a druga pointer na neki nas objekat, ili obe primaju pointere na razlicite objekte, npr:

void Bu(int nParametar);
void Bu(Pravac * pPravac);
void Bu(Neshto * pNeshto);

kako onda pozivamo zeljenu funkciju ako zelimo da joj prosledimo NULL pointer? :)
Koja ce biti pozvana od ove tri, ako pravilo postoji?

2.) Kojim redosledom objekti umiru kada izadju iz scope-a?
Npr //class A{}; class B{};

void main()
{
A a; B b;
}

Dal' prvo umire a ili b? U VC6.0 kompajleru prvo se poziva konstruktor onog koji je poslednji kreiran.

Ovako ne izgleda preterano bitna stvar ali cini mi se da je primer jednog konkretnog problema sledeci (ne garantujem, pricam napamet, secam se konkretnog problema ali tad o ovome nisam razmisljao a mrzi me da dignem vs da proverim ;) )

{
CDC MemDC;
CBitmap Bitmapa;
... bla bla inicijalizacije ...
MemDC.SelectObject(&Bitmapa)
... bla bla poso ...
}

u ovom momentu prvo umire bitmapa pa onda MemDC sa postojecim pointerom na bitmapu koja ne postoji, a moguce je da u destruktoru radi nesto sa istom jer kod kolko se secam puca ako se ne sacuva stara i vrati na kraju tj:

CBitmap * pOld = MemDC.SelectObject(&Bitmapa);
... bla bla ...
MemDC.SelectObject(pOld);


Postoji li neka specifikacija koja govori tacno o tom redosledu?

E da, i ako imamo vise cpp fajlova i globalne promenljive (objekte) u istima, kojim se redosledom inicijalizuju?




3.) Ovo moram da ilustrujem:

.h
class Problem ()
{
static int s_nLastID;

int m_nID;
Podaci m_Podatkovi;

Problem()
{
m_nID = ++s_nLastID; // inicijalizacija
}
Problem(const Problem & p)
{
<inicijalizacija>;
*this = p;
}
Problem & operator = (const Problem & eq)
{
m_Podatkovi = eq.m_Podatkovi;
return *this;
}
};

.cpp
int Problem::s_nLastID = 0;

Znaci rec je o klasi koja prilikom instanciranja markira svakom objektu unique m_nID a ostale podatke ili ne dira (default) ili kopira.
E sad, na mestu <inicijalizacija>; moze stajati kod iz (default, ako se tako zove?) konstruktora ili oba konstruktora mogu pozivati neku nasu funkciju Initialize() ili Construct().
U ovom primeru to nije problem - inicijalizacija je duga jedan red, ali ako je duga 10tak redova i postoje 3 copy konstruktora onda je jedino resenje pravljenje Construct() funkcije sto je opet malo ruznjikavo.

Pitanje je dal' postoji nacin da se iz copy konstruktora prvo pozove kod iz default konstruktora ili mora da se pravi odvojena funkcija?

Da,
Problem(const Problem & p)
{
Problem();
*this = p;
}
ne radi poso, i izaziva s***** ako se pretpostavi da radi, kao sto sam ja pretpostavio i smarao se par sati dok se nisam lupio po chelu i setio (obrnutim redom) da red Problem(); trenutno kreira objekat tog tipa a ne poziva kod za trenutni...

Nadam se da nisam smorio :)
[ Dragi Tata @ 19.03.2002. 16:24 ] @
Ne znam da li si "smorio" (mnogo popularna reč na ES-u, a samo nagađam šta znači) ali jesi malo opširan. Tako da ću sada da ti odgovorim na 1) a kad stignem i na ostala podpitanja.

Najpre, one funkcije koje si naveo kao primer nisu polimorfne. Polimorfizam je nešto drugo, a one su "overloaded" (nemam pojma koji termin se koristi u srpskom za ovo). Uglavnom, ako probaš da pozoveš funkciju Bu(NULL), kompajler će da prijavi grešku, jer nije u stanju da razluči koju verziju funkcije Bu da pozove. Na engleskom se to kaže "ambiguous call".

Nastavak sledi.
[ Dragi Tata @ 20.03.2002. 16:09 ] @
Evo ti odgovor na 2)

Kada deklarišeš lokalne promenljive u nekom opsegu, one se inicijalizuju onim redom kojim su navedene (u tvom primeru a pa b) i smeštaju se na stack. E, kad se opseg završi, onda se jedna po jedna "skidaju" sa stack-a i odlaze u zaborav. Kao što verovatno znaš, za stack je karakterističan redosled "prvi unutra, poslednji napolje" te b odlazi prvi pa tek onda a.

Ako govorimo o globalnim promenljivama koje su definisane u raznim fajlovima, ne postoji nikakva garancija da će jedna promenljiva da bude inicijalizovana pre druge. Jednostavno, u tom slučaju nikad ne znaš redosled.

Nastavak sledi...
[ Dragi Tata @ 20.03.2002. 20:55 ] @
Najzad 3)

Ne znam način da se "kulturno" pozove kod default konstruktora iz copy konstruktora. Postoje hack-ovi, kao što je

Code:

struct a
    {
    int ia;

    a() {ia = 10;}
    a(const a& other)
        {
        a temp;
        *this = temp;
...    ovde kopiraj  vrednost iz other
        }
    };


ali je to toliko ružno i toliko je lako napraviti grešku sa tim, da odmah idem u WC da operam ruke pošto sam napisao tako nešto.

Jednostavno, kad malo razmisliš, copy konstruktor treba da iskopira vrednosti članica iz drugog objekta, a podrazumevani (mislim da je ovo dobar prevod za default) konstruktor služi za inicijalizaciju objekta "od nule". Primer koji si napisao je jedna od jako retkih situacija gde ti može zatrebati da pozoveš podrazumevani iz copy, ali u tom slučaju bolje napravi neku private funkciju Init u koju ćeš da smestiš "zajednički" kod.

Najzad, pošto mi se jako dopadaju tvoja pitanja, evo ti za nagradu jedan lep link gde možeš da nađeš puno štoseva o konstruktorima i destruktorima:

http://cpptips.hyperformix.com/Ctordtor.html
[ Dejan Lozanovic @ 22.03.2002. 21:18 ] @
Citat:
fili:
1.) Kada imamo na primer dve polimorfne funkcije od kojih jedna kao parametar prima int a druga pointer na neki nas objekat, ili obe primaju pointere na razlicite objekte, npr:



void Bu(int nParametar);

void Bu(Pravac * pPravac);

void Bu(Neshto * pNeshto);



kako onda pozivamo zeljenu funkciju ako zelimo da joj prosledimo NULL pointer? :)

Koja ce biti pozvana od ove tri, ako pravilo postoji?


Na 2. i 3. pitanje dragi tata je lepo i detaljno odgovorio. A ja cu da ti odgovorim na prvo. Kao sto je dragi tata rekao, u pitanju su "preklopljene" funkcije, e sada sa Bu(NULL) bi dobio gresku. jer C++ kompajler ne zna da konvertuje NULL u nesto ali ako bi napisao nesto tipa
Bu( (Pravac*) NULL); E tada je vec druga prica, jer tada C++ kompajler explicitno pravi konverziju i poziva drugu funkciju. Mozes da napravis operatore za inmplicitnu koncerziju klase NestoSasvimTrece u klasu Pravac i tada pokazivac na NestoSasvimTrece *A mozes slobodno da pozoves
Bu(A); i kompajler nece prijaviti gresku, ali ako to NestoSasvimTrece postoji i definiasna imlicitna konvezija u klasu Nesto e tada ce biti prijavljena greska jer opet kompajler ne zna koju konverziju da primeni. Opet da na pomenem da kompajler ako ima konverziju iz tipa A u tip B, i iz tipa B u tip C, i imas f-ju koja poziva func(C arg); sa

pozivom funkcije func(pA) ces dobiti gresku jer ne mogu lancano implicitne konverzije da se prave.

a ono pitanje pod 2, recimo sto se nebi zaigrao i napravaio klase koje u destruktoru kazu ko su sta su i odakle su, i onda kreiras objekte tih klasa i igras se i testiras kako koji "umire" :)
[ Le Piaf @ 23.03.2002. 09:49 ] @
Citat:
fili:

void Bu(int nParametar);
void Bu(Pravac * pPravac);
void Bu(Neshto * pNeshto);

kako onda pozivamo zeljenu funkciju ako zelimo da joj prosledimo NULL pointer? :)
Koja ce biti pozvana od ove tri, ako pravilo postoji?



Najprostiji nachin (da ne kazem adhoc reshenje :) bi bilo mozda neshto ovako:
Pravac *pPr=NULL;
Bu(pPr);

cya

p.s. mada mislim da je ono shto je SystemOut predlozio sa Bu((Pravac*)NULL) najelegantnije
[ fili @ 24.03.2002. 14:35 ] @
Citat:
SyStemOuT:
Bu( (Pravac*) NULL); E tada je vec druga prica, jer tada C++ kompajler explicitno pravi konverziju i poziva drugu funkciju. Mozes da napravis operatore za


Da da, hvala, palo mi je to napamet pola sekunde poshto sam postovao pitanje :)


Citat:
SyStemOuT:
a ono pitanje pod 2, recimo sto se nebi zaigrao i napravaio klase koje u destruktoru kazu ko su sta su i odakle su, i onda kreiras objekte tih klasa i igras se i testiras kako koji "umire" :)


Ma to sam i uradio, i napisao sam u poruci da vidim da u VisualC++u to tako radi, ali nisam bio siguran da li je to univerzalna situacija. Dragi tata me je lepo podsetio da to sve ide na stek pa sad kontam da je zagarantovano da uvek onaj koji se prvi kreira poslednji umire. :)


Imao sam josh jedno dobro pitanje pre par dana, al ne mogu da ga se setim.. secam se samo da je bilo kul.. ;)
[ fili @ 26.03.2002. 13:58 ] @
Ne znam dal' se neko odavde zanima za C#?
Ja eto imam neko slobodno vreme pa sam uzeo malo da gledam na šta to liči, i liči mi, možda ću se i udubiti u temu. Elem, zašto pišem poruku ovde - naleteo sam na sample koji pokazuje kako je u ce šarpu rešeno ono sa konstruktorima:

Code:

// C#
class AShape
{
    private int m_answer = 42;
    // Forward to another constructor.
    public AShape() 
    : this(System.Drawing.Color.Red, 5280, DefinedSizes.Large)
    {
        // Additional initialization goes here.
    }

    public AShape(Color color, int length, DefinedSizes size)
    {
        // Code to initialize the class goes here.
    }
}


Fino, zar ne? :)

Ps, kako da izvedem da se vide tabulatori ili razmaci unutar
Code:
?
Sve ispada slepljeno uz levu ivicu, iako tako nije napisano?