C++ öröklődés constructor alapértelmezett paraméter

 ( dejo | 2009. június 21., vasárnap - 10:44 )

Egy ismerősöm kért meg, hogy segítsek neki C++-ban a téma szerinti dolgokat megérteni.
Én sem programozok C++-ban. Ő azt mondta, hogy a tanáruk adott egy feladatot, amit ha megoldanak, akkor jó eséllyel le tudnak vizsgázni.
Én ezt a megoldást írtam:

#include 
#include 


using namespace std;

// szaitogep osztaly
class Cszamitogep
{
protected:
   double cpuGHz, hddGB, S;
   bool  magyarkbd;

public:
// szaitogep osztaly constructora
   Cszamitogep(double cpu_GHz =1.8, double hdd_GB = 80, bool magyar_kbd = true)
   {
      cpuGHz = cpu_GHz;
      hddGB = hdd_GB;
      magyarkbd = magyar_kbd;
   }

// szaitogep osztaly destructora
   ~Cszamitogep()
   {
   }

// masolasi idoigeny kiszamitas és visszaadsa
   double idoigeny (int X)
   {
      S=(100*(1024^2));
      return (X/S);
   }
};


// Claptop osztaly a Cszamitogep osztalybol szarmaztatva
class Claptop : public Cszamitogep
{
private:
    bool  Sata;
    double S;


public:
// Claptop osztaly constructora
   Claptop( bool Sata_e = true, double cpu_GHz =1.8, double hdd_GB = 80, bool magyar_kbd = true) : Cszamitogep()
   {
       Sata = Sata_e;
   }

// Claptop osztaly destructora
   ~Claptop()
   {
   }

// masolasi idoigeny kiszamitas és visszaadsa
   double idoigeny (int X)
   {
      S=70*(1024^2);
      if (Sata)
          S=(S*log(S));
      return (X/S);
   }

};


int main(){
    cout << "Egy számítógép magadása" << endl;
    Cszamitogep  szamitogep1(2.5, 320, true);
    cout << "Egy számítógép 100 MB átvitelének idoigenye:" << szamitogep1.idoigeny(100*(1024^2))  << endl;

    cout << "----------" << endl;
    cout << "Egy Satas laptop magadása" << endl;
    Claptop  laptop1(true, 1.7, 120, true);
    cout << "Egy Satas laptop 70 MB átvitelének idoigenye:" << laptop1.idoigeny(70*(1024^2))  << endl;

    cout << "----------" << endl;
    cout << "Egy IDE-s laptop magadása" << endl;
    Claptop  laptop2(false, 1.7, 120, true);
    cout << "Egy IDE-s laptop 70 MB átvitelének idoigenye:" << laptop2.idoigeny(70*(1024^2))  << endl;



    getchar();
    return 0;
}

Ez így most működik, de a Claptop osztálynál szerintem nem így kell a constructort elkészíteni, hiszen így fölöslegesen ismétlem meg a paramétereket, azaz egyúttal túlterhelem, ami itt ugyancsak fölösleges.
Próbáltam így:
Claptop( bool Sata_e = true) : Cszamitogep()
De akkor hogy kell helyesen paraméterezni a laptp1 objektum létrehozásához, mert a main()-ban fent megadott módon, csak egy paramétert fogad el?

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Hirtelen most ezt dobtam össze:

http://hup.pastebin.com/f827c539

Amit kiemelnék :
1, const-k használata
2, virtual dtor a bázisosztályban, virtual legyen az idoigeny is! ( jelen esetben nem sokat jelent, de ha pointerekkel dolgozunk, akkor érdekes eredmények jöhettek volna ki! )
3, inicializáljunk a ctor-ban, mintsem a blokkban értékadjunk
4, az S paraméter mivel konstans, érdemes konstansként megadni.

Minden jót.

Köszi, tanulmányozom. Úgy látom van minek utána néznem.
Amúgy néhány helyen megnéztem ezt a C++ constructor dolgot és többféle megoldást láttam. Én a számomra legérthetőbbet próbáltam alkalmazni, csak ebből nem volt öröklődéses példa arra az esetre amikor a kezdőparamétereket is meg lehet adni.
--
не закурить! (Ne gyújts rá!) не куриться! (Ne dohányozz! Ne füstölögj!)

Nem értem, hogyan működhet, mikor ez így nem jó.

Neked is köszönöm.
Igen sejtettem, hogy nem jó, azért is kértrem segítséget.
A Code::Blocks MinGW-s változatában (Windowson) mindenesetre lefordul és látszólag azt csinálja amit kell.
Esetleg ha konkrétabban is leírnád miben látod a programocska hibáját, hibáit, azzal sokat segítenél.
--
не закурить! (Ne gyújts rá!) не куриться! (Ne dohányozz! Ne füstölögj!)

Miután létrehoztad laptop1-et és laptop2-t szándékod szerint 1.7GHz-vel illetve 120GB-tal, nézd meg, hogy a létrejött objektumokban mennyi GHz és GB van.

Mivel ez nem volt a feladatban nem próbáltam ki, de igazad lehet!
--
не закурить! (Ne gyújts rá!) не куриться! (Ne dohányozz! Ne füstölögj!)

Claptop( bool Sata_e = true, double cpu_GHz =1.8, double hdd_GB = 80, bool magyar_kbd = true) : Cszamitogep()
Itt alapertelmezett konstruktort hivsz meg, pedig nem is adtal meg olyant a bazisosztalyban. Legyen inkabb igy:
Claptop( bool Sata_e = true, double cpu_GHz =1.8, double hdd_GB = 80, bool magyar_kbd = true) : Cszamitogep(cpu_GHz, hdd_GB, magyar_kbd)

De megadott... Az alapértelmezett paraméterekkel.
A gond ott van, hogy ha más paraméterekkel hívja meg a leszármazottat, akkor is az alapértelmezett hívódik...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Tényleg, ezt nem vettem észre, ezért nem értettem, hogy miért működik így. :)

Világosodik. Igazából eredetileg mint írtam a kérdésem végén én így képzeltem:
Claptop( bool Sata_e = true) : Cszamitogep()
Arra gondoltam, hogy mivel örökli a Cszamitogep osztályból, ezért a paraméterek az átvétellel együtt öröklődnek és itt már csak a bővítést (Sata_e) kell átvennem. De mert így nem sikerült a megfelelő paraméterekkel létrehozni a laptop1, laptop2 objektumokat, ezért soroltam fel a korábbi ősosztály paramétereit, de határozottan éreztem, hogy ez így nem jó. Tehát át is kell venni!

Azt nem lehet kikerülni azt, hogy az ősosztály paraméterei újbóli megadás nélkül bekerüljenek a származtatott osztályba?
--
не закурить! (Ne gyújts rá!) не куриться! (Ne dohányozz! Ne füstölögj!)

"Azt nem lehet kikerülni azt, hogy az ősosztály paraméterei újbóli megadás nélkül bekerüljenek a származtatott osztályba?"

Mármint arra gondolsz, hogy az gyerekosztály egyik konstruktorának ugyanazok legyenek a default paraméterei, mint a szülőosztály egyik konstruktorának, miközben a két konstruktor paraméterlistája különbözik? Nem, azt nem lehet.

Igen erre gondoltam. Jelen példa esetében van ugye három paramétere az ős osztálynak a Cszamitogep-nek és a belőle származtatott Claptop-nak ugyanaz a három paramétere van és még azon kívül eggyel bővítettem. Tehát amikor a Claptop osztályból létrehozom a laptop1 objektumot, akkor számomra logikus lenne, hogy mind a négy paramétert megadjam. De úgy látom, hogy az ősosztály konstruktoranak, így nem tudok paramétert átadni (vagy csak azt nem tudom, hogyan kell azaz mi a syntaxisa) csak, ha kiegészítem vele a gyermek osztály konstruktorát. Ami a bosszantó, hogy ezeket az ősosztálybeli paramétereket újból át is kell venni. Akkor miért is jó, az öröklés, ha nem azért, hogy azokat a dolgokat amit az ősosztályhoz képest nem akarok változtatni (azt a három paramétert nem akarom, és itt nem csak a kezdeti értékére gondolok), ne kelljen újból lekódolni? Jó a metódusokat minősítéssel tudom hívogatni.

--
не закурить! (Ne gyújts rá!) не куриться! (Ne dohányozz! Ne füstölögj!)

Tehát amikor a Claptop osztályból létrehozom a laptop1 objektumot, akkor számomra logikus lenne, hogy mind a négy paramétert megadjam. De úgy látom, hogy az ősosztály konstruktoranak, így nem tudok paramétert átadni (vagy csak azt nem tudom, hogyan kell azaz mi a syntaxisa)

Így:

//...
public:
// Claptop osztaly constructora
   Claptop( bool Sata_e = true, double cpu_GHz =1.8, double hdd_GB = 80, bool magyar_kbd = true) : Cszamitogep(cpu_Ghz, hdd_GB, magyar_kbd)
   {
       Sata = Sata_e;
   }
//...

Így a Claptop objektum konstruktorának meghívásakor meghívódik az ősosztály konstruktora a megfelelő paraméterekkel, és a Claptop konstruktorába csak azokat a változókat kell beállítani, amit nem végez el az ősosztály konstruktora.
Remélem erre gondoltál.

Aha! Tehát a paramétereket újból felsorolom, de az ősosztály constructorának hívásában is jelzem, hogy ez onnan származik.
Itt: "Cszamitogep(cpu_Ghz, hdd_GB, magyar_kbd)" ezek formális paraméterek?
ezek "Claptop( bool Sata_e = true, double cpu_GHz =1.8, double hdd_GB = 80, bool magyar_kbd = true)" párja?

--
не закурить! (Ne gyújts rá!) не куриться! (Ne dohányozz! Ne füstölögj!)

Hogy érted azt, hogy formális paraméterek-e?
Azok azok a paraméterek, amiket a Claptop konstruktora megkap, és "továbbadja" az ősosztály konstruktorának.

Szerk.: Közben módosítottál.
Igen, azok "párja".
de ha a cpu_GHz helyett kiskutyafülét írsz, úgy is működni fog csak akkor így kell írni:

Claptop( bool Sata_e = true, double kiskutyafüle =1.8, double hdd_GB = 80, bool magyar_kbd = true) : Cszamitogep(kiskutyafüle, hdd_GB, magyar_kbd)

Úgy értem, hogy formális, mert meg kell egyezniük a neveknek, azokkal amiket a másik sorban kövérrel írtam.
Ugyanakkor pozicionális paraméterek is az ősosztály konstruktorában felsorolt sorrendnek megfelelően.
szerk: Közben te is kiegészítetted. Ugyanarról beszélünk.
--
не закурить! (Ne gyújts rá!) не куриться! (Ne dohányozz! Ne füstölögj!)

Nem. Lehet az akár cpp_GHz+5.

Ebben az esetben a cpu_GHz+5 így egyben az egész kifejezés, pozicionális paramétere az ősosztálynak, de cpu_GHz magában formális paramétere a gyermek osztálynak.
Feltéve, ha a fenti kódrészlet ezen részéról beszélünk: ": Cszamitogep(cpu_Ghz, hdd_GB, magyar_kbd)"

Ugye te is így gondolod?
--
не закурить! (Ne gyújts rá!) не куриться! (Ne dohányozz! Ne füstölögj!)

Nem értem ezeket a varázsszavakat, amiket használsz, de lehetnek pl. ilyenek:

": Cszamitogep(cpu_Ghz, hdd_GB, magyar_kbd)"
": Cszamitogep(cpu_Ghz+5, 6-hdd_GB, magyar_kbd)"
": Cszamitogep(2.5, hdd_GB, false)"
": Cszamitogep(hdd_GB, cpu_Ghz)"

Nem, azok aktuális paraméterek.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

1:
A publikus öröklés célja az "az egy" kapcsolatok kifejezése. (Claptop az egy Cszamitogep).
_NEM_ célja viszont a kód újrafelhasználása, ez maximum mellékhatás.

2:
Egy objektum a következőképp jön létre:
Lefoglalódik a memória, majd létrejön az szülő osztály (ha van), majd a konkrét osztály (lefut a konstruktor törzse). Ez rekurzív, azaz ha a szülő osztálynak van szülője, akkor előbb az jön létre, stb...

Ha nincs más megadva, a default konstruktor hívódik. (A default konstruktor a paraméter nélküli konstruktor. Ha ilyen nincs, de van olyan paraméteres konstruktor, melynek minden paramétere rendelkezik default értékkel, akkor az hívódik. Ha ilyen nincs, és mi sem írtunk egyet, akkor a fordító csinál egyet.)

class Claptop {
...
    Claptop(...) 
       : Cszamitogep()
    {...}
};

Ennek nincs sok teteje, mert a : Cszamitogep() nélkül is ugyanaz történik. (Nem szokás kiírni.)

class Claptop {
...
    Claptop(...) : Cszamitogep(12,33,22)
    {...}
};

Ilyenkor a Cszamitogep 3 paraméteres konstruktora hívódik meg.

class Claptop {
...
    Claptop(int a,int b,double c) : Cszamitogep(a,b,c)
    {...}
};

Ilyenkor a Cszamitogep 3 paraméteres konstruktora hívódik meg a, b és c értékekkel.

class Claptop {
...
    Claptop(int a=1,int b=2,double c=3) : Cszamitogep(a,b,c)
    {...}
};

Ez ugyanaz mint az előző, csak a Claptop 3 paraméteres konstruktorának vannak default paraméterei.

3:
Az osztályneveidtől kiráz a hideg. Az MS százéves API-jain kívül senki nem jelöli meg külön betűvel az osztályokat, maximum nagybetűvel kezdődnek. De még ha van is valamilyen okból valamilyen prefix, akkor is utána a következő szó nagybetű, azaz Laptop vagy CLaptop, de nem Claptop.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Először is köszönöm a részletes kifejtést.
Ami a 3-as pontot illeti arra azt tudom mondani, hogy ezt teszi a "két napos" tehát igen régi rutin! :-{)E
--
не закурить! (Ne gyújts rá!) не куриться! (Ne dohányozz! Ne füstölögj!)

Mivel a Claptop konstruktorának meghívásakor nem adod át a megadott paramétereket a Cszamitogep konstruktorának, az az alapértelmezett értékeket fogja eltárolni. (olyan mintha úgy hoztad volna létre a Claptop objektumot, hogy egy paramétert sem adtál volna meg neki...).

Szerk.: Megelőztek ;)

Hibás a következő sor is:
S=70*(1024^2);
^ C-ben a kizáró vagy jele (xor), nem a négyzetreemelésé.

huhh, ezen át is futottam, javítottam a pastebin-ben. ez a copypaste hatása ( antipattern vagy vmi..:S )

Nem ezt én szúrtam el! Alapból azt gondoltam, hogy a ^ jel a négyzetre emelés.
--
не закурить! (Ne gyújts rá!) не куриться! (Ne dohányozz! Ne füstölögj!)

Mit gondoltok?
Ha most kedvet kapnék C++-t tanulni, akkor ez a tankönyv mennyire megfelelő: Objektum orientált szoftverfejlesztés
Úgy látom nem túl matekos, de nem is nagyon laikus. Talán pont jó lenne.
--
не закурить! (Ne gyújts rá!) не куриться! (Ne dohányozz! Ne füstölögj!)

Ez sok mindentől függ, pl.: tudsz-e programozni, ha igen, milyen nyelven?

Erről a könyvről sose hallottam. Az biztos mellette szól, hogy ingyenes. :)

Én nem szeretem az olyan könyvet ami ennyire az elmélet oldaláról közelít (az első 5 fejezetben nincs forrás), ez nekem kicsit manager megközelítés. :)
Eleve úgy tűnik számomra, hogy azoknak íródott, akik már tudnak programozni (leginkább C-ben), és ismerik a programozás elméletét is valamennyire.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Az túlzás, hogy tudok programozni, persze a kérdés, hogy mihez képest?
Régebben tizen évekkel ezelőtt írtam ügyviteli programokat Clipperben. (Némelyiket még ma is használják.) Aztán meg néhány programot (iktatás, menetlevél elszámoltatás, kamarai tagnyilvántartó) Delphiben Paradox táblákkal.
Iskolában is tanultam egy-egy félévben Pascalt, Javat, C-t, Visual C++-t, Visual Basic-et. Egy kicsi Pythont, egy kicsi SQL-t, Egy kicsi PHP-t. De ezek csak arra voltak jók, hogy a vizsgákat teljesítsem. Infó szigorlaton pont a C++ öröklést húztam. Papírra felírtam egy szülő osztályt, meg egy gyermek osztályt, aztán csináltam is belőlük egy egy példány objektumot, hívtam egy-egy metódusát is a példa kedvéért, a kapcsolódó kérdésekre tudtam a választ és ez akkor ötös volt.
No de a gyakorlatban ez kevés. Arra jó, hogy az elvet ismerjem, de a megvalósítás finomságain elakadok. Tudom gyakorolni, gyakorolni, meg alaposabban elmélyedni a részletekben.
Valami platform független dolgot szeretnék, nem biztos, hogy a C++ a legjobb választás.
PHP-ben és Pythonban próbálkoztam, de az interaktív felhasználói felület a probléma mindenütt. Pl. a PHP nem is vészes, de kellene hozzá mondjuk az AJAX, amit nem igazán látok át.
Python, C++, Java esetében meg valamilyen GUI kiegészítés. Tk, Qt, GTK+, WX, stb.. Szóval elég nagy a kínálat és ezekről sajnos magyarul, nem nagyon van doksi.
No csak azt akarom mondani, hogy egyre jobban látom mi az, és főleg milyen sok az amit még nem tudok.
--
не закурить! (Ne gyújts rá!) не куриться! (Ne dohányozz! Ne füstölögj!)

Sose értettem, minek tanítanak egy iskolában 6 nyelvet sehogy ahelyett, hogy egyet rendesen. Főleg mivel egy nyelv (pl C++) ismeretében egy következőt megtanulni (alap szinten) gyerekjáték...

A C++ nyelv nehéz. Az egyik legnehezebb.
Ha könnyen és gyorsan akarsz sikerélményhez jutni, akkor C#, Java, Python a te barátod...

(Igen, Linuxon C#. Maga a nyelv szépen támogatott monoban, minimális lemaradás az Ms-hez képest. A libekkel (.Net) nem ilyen rózsás a helyzet, de túlélhető, nomeg nem kötelező használni (qt, gtk van))

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Sziasztok!

Nekem egy egesz egyszeru problemam van, de nem igazan talalok ra sehol egy egyszeru peldat ami bemutatna a megoldast.
A szitu egyszeru: a.cpp fajlban el van keszitve egy osztaly, konstruktorral, fuggvenyekkel.
b.cpp fajlban letrehozok egy peldanyt az osztalybol, itt egyszeruen tudom futtatni az osztalyban levo fuggvenyeket. Viszont a b.cpp-ben letrehozott osztalyt szeretnem elerni c.cpp -ben is.

Pelda:
Client *client; (headerben deklaralva)

client = new Client();
client->ertek("20");

Szoval szeretnem ha a client peldany egy masik fajlban levo kodbol is elerheto lenne.
Esetleg egy egyszeru pelda sokat segiteni!:)

Koszonom!

Nem biztos, hogy jól értem a kérdést, de ugye az a lényeg, hogy a programnak van egy folyása.

Ennek során te létrehozol egy példányt.

Ha a program folyása során később egy másik fájlban leírt programrészben használni akarod ezt a példányt, akkor ehhez az kell, hogy itt (c.cpp) valami módon a példány elérhető legyen.

Erre a legcsúnyább megoldás az, ha globális változót definiálsz, és azt valahol feltöltve el fogod érni máshol is.
Úgy veszem ki a leírásodból, hogy épp ezt akarod.
A headerben deklaráltad, innentől kezdve bárhol hivatkozhatsz rá, de persze a te felelősséged, hogy valahol definiáld, és ez hamarabb történjék, minthogy máshol hivatkozol rá.

Ennél jobb lenne például, ha a c.cpp-ben definiált C osztály, ami használni akarja az A példányát, paraméterül kapná (akár konstruktorban, akár úgy, hogy amikor meghívod az adott tagfüggvényt, amivel a c.cpp-be kerül a vezérlés, akkor annak a függvénynek bemenő paramétere).

En nekem olyan elkepzelesem van, hogy csinal az A-ban egy privat statik valtozot, meg mondjuk egy getInstance() statik fuggvenyt, a getInstance is csinalhat uj peldanyu osztalyt, letarolja, es mindig ugyanazt a peldanyt adja vissza a statik privatbol. Ekkor mar majdnem singleton a cucc.

class A {
  public:
    A(int param1, double param2, string param3) {}
    static A *getInstance() {
        if(m_instance == NULL)
            m_instance = new A(1,2,"x");
        return m_instance;
    }
  private:
    static A *m_instance;
};

Ez igy egy kvazi-globalis megoldas, hiszen mindig ugyanazt az instancot adja vissza, viszont megse globalis valtozo.
Persze nem vagyok C++ guru, igy kover syntax errorok vigyoroghatnak a kodban. Semmikepp ne 1-1 hasznald fel.
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

"Ekkor mar majdnem singleton a cucc."

Szerintem nem csak majdnem...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Szerenytelenul allithatom: szereny vagyok. :-)
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Ha van publikus konstruktor, akkor hogyan lehetne singleton? Singletonok eseteben nem szabad publikus konstruktornak lennie, hiszen ekkor barki peldanyosithat meg n peldanyt az adott osztalybol.

Akkor a construktor lehet privat is, ekkor azonban garantalni kell, hogy senki nem akar konstruktoron at hivogatni. Raadasul minden konstruktortipushoz (ugy errtem, minden kulonbozo parameterezesu konstruktorhoz) kell egy getinstance, amit raadasul rendesen meg kell irni.
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Szerintem nem.

?
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Ha singleton, akkor egy példány van belőle, a függvénynek mindig azt az egy példányt kell visszaadnia, innentől nincs sok értelme a különféle paraméterezésnek.

Annak nincs, viszont ugy meg lehet csinalni, hogy a tarolt peldany megletetol fuggoen vagy a parameterek menten csinal egy ujat (azaz meghivja a megfelelo parametert), vagy (es itt feltetelezem, hogy a feladat ezt megengedi) az adott peldanyt felkonfiguralja a kapott parameterek menten, es ugy adja vissza (megkimelve egy kalap set*() cimu fuggvenyhivastol a kliens kodot).
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Ez már szőrözés. :)

Egyébként meg miért ne lehetne olyan az osztályom amiből van egy globális, illetve van értelme lokálist létrehozni?

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

A csúnya megoldás az, ha globális változót deklarálsz.

Ha a .h fájlba csak simán beraksz egy "Client *client;"-et, szerintem linkeléskor hibát kapsz (két Client pointer azonos néven). Extern a barátod.

De ahogy gee is írta fentebb, ezzel az a baj, hogy normális esetben nem tudod garantálni, hogy amikor te hivatkozol a *client-re, addigra az már létrejött...
Erre a megoldás a Singleton.
Google rengeteg egyéb találatot ad rá...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Koszi szepen, a singleton-os megoldas szuperul bevalt!:)