1 / 32

Jazyk C++

Jazyk C++.

kerri
Télécharger la présentation

Jazyk C++

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Jazyk C++ B. Stroustrup začal na "C se třídami" pracovat v roce 1979 v rámci své PhD. práce. Potřeboval jazyk, který by byl výhodný pro psaní velkých projektů (Simula), ovšem také rychlý. Při práci v AT&T Bellových laboratořích přišel do kontaktu s C, ve kterém bylo psáno jádro UNIXu. K tomuto jazyku přidal vlastnosti jazyku Simula se zachováním všech původních vlastností. V roce 1983 se nový jazyk začal nazývat C++ (kde inkrementační operátor ++ má naznačit revoluční posun vpřed) a v roce 1998 byl jazyk standardizován (ISO/IEC 14882:1998). Evropský sociální fond Praha & EU: Investujeme do vaší budoucnosti Bjarne Stroustrup 1950 - Pro projekt „Cesta k vědě“ (veda.gymjs.net) vytvořil V. Pospíšil (gdermog@seznam.cz). Modifikace a šíření dokumentu podléhá licenci CC-BY-SA.

  2. Objektově orientované programování (OOP) Objekty – jednotlivé prvky (jak data, tak související funkčnost) jsou v programu seskupeny do entit, nazývaných objekty. Objekty si pamatují svůj stav a navenek poskytují operace. Abstrakce – Každý objekt pracuje jako černá skříňka, která dokáže provádět určené činnosti a komunikovat s okolím, aniž by vyžadovala znalost způsobu, kterým vnitřně pracuje. Zapouzdření – zaručuje, že objekt nemůže přímo přistupovat k „vnitřnostem“ jiných objektů. Každý objekt navenek zpřístupňuje rozhraní, pomocí se s objektem pracuje. Skládání – Objekt může obsahovat jiné objekty. Delegování – Objekt může využívat služeb jiných objektů tak, že je požádá o provedení operace. Dědičnost – objekty jsou organizovány stromovým způsobem, kdy objekty nějakého druhu mohou dědit z jiného druhu objektů, čímž přebírají jejich schopnosti, ke kterým pouze přidávají svoje vlastní rozšíření. Tato myšlenka je implementována pomocí rozdělení objektů do tříd, přičemž každý objekt je instancí nějaké třídy. Každá třída pak může dědit od jiných tříd. Polymorfismus – odkazovaný objekt se chová podle toho, jaké třídy je instancí. Pokud několik objektů poskytuje stejné rozhraní, pracuje se s nimi stejným způsobem, ale jejich konkrétní chování se liší podle implementace. U polymorfismu podmíněného dědičností to znamená, že na místo, kde je očekávána instance nějaké třídy, můžeme dosadit i instanci jejího libovolného předka, neboť rozhraní předka je podmnožinou rozhraní třídy. U polymorfismu nepodmíněného dědičností je dostačující, jestliže se rozhraní (nebo jejich požadované části) u různých tříd shodují, pak jsou vzájemně polymorfní.

  3. Poznámka k použitým příkladům Všechny příklady v této přednášce se budou motat kolem fyzikálního problému - konkrétně simulace rozvoje elektromagnetické spršky v solidním bloku olova metodou Monte Carlo. Simulace fyzikálních jevů je velice mocný nástroj, který umožňuje předvídat chování detektorových soustav při experimentálním provozu a dovoluje tak optimalizovat jejich konstrukci a nalezení systematických chyb. Návrhu a stavbě každého detektoru předchází období simulací, kdy jsou na základě již známých fyzikálních jevů modelována data, která bude detektor sbírat. Tato data jsou pak zpracována stejným systémem, kterým budou později zpracovávána data z reálných experimentů. Protože jsou tak známa jak vstupní data, tak výsledky, lze získat mnoho informací o efektivitě procesu zpracování dat. Příklad EM spršky v mlžné komoře iniciované elektronem kosmického záření v mg. poli (1959) Simulace EM spršky je samozřejmě velmi komplexní a není obsahem této přednášky - formou příkladů k jednotlivým úhelným kamenům OOP bude ale naznačen její princip.

  4. Poznámka k použitým příkladům Všechny příklady v této přednášce se budou motat kolem fyzikálního problému - konkrétně simulace rozvoje elektromagnetické spršky v solidním bloku olova metodou Monte Carlo. Dostatečně energetický foton může v poli těžkého jádra všechnu svoji energii přeměnit na pár elektron-pozitron. Energie nad 2x mec2 je přeměněna na kinetickou energii produktů. Fotonové interakce Při fotoefektu spotřebuje foton všechnu svou energii na vyražení elektronu z obalu atomu. Elektron získá všechnu energii fotonu zmenšenou o vazebnou energii. Na elektronech s nízkou vazebnou energií dochází k Comptonově rozptylu - foton se srazí s elektronem jako by byl hmotný bod a předá mu část své kinetické energie. Odražený foton má nižší frekvenci.

  5. Poznámka k použitým příkladům Všechny příklady v této přednášce se budou motat kolem fyzikálního problému - konkrétně simulace rozvoje elektromagnetické spršky v solidním bloku olova metodou Monte Carlo. Elektronové interakce Tvrdé kolize pozitronů a elektronů s atomy způsobují vyrážení elektronů z obalů. Ty mohou nabrat takovou energii, že pak samy vyrážejí další elektrony z obalů dalších atomů. Pokud nabitá částice prochází zrychlením (jakýmkoliv), emituje fotony. Tyto fotony se nazývají Brzdné záření (bremsstrahlung). Elektrony ztrácejí energii přes elektromagnetické interakce s ostatními elektrony a jádry (excitace).

  6. Poznámka k použitým příkladům Všechny příklady v této přednášce se budou motat kolem fyzikálního problému - konkrétně simulace rozvoje elektromagnetické spršky v solidním bloku olova metodou Monte Carlo. PHT TR Foton PHT BZ TR AN Elektron TR TR TP BZ BZ Pozitron TR CR BZ PHT Fotoefekt BZ TP Tvorba párů BZ Brzdné záření CR AN CR Compton BZ TP TR Tvrdý rozptyl BZ TR AN Anihilace Primární foton PHT TR TR

  7. Třída a objekt Třída je datový typ, který principiálně rozšiřuje datové struktury (struct). Kromě dat obsahuje také procedury, které s daty pracují. Deklaruje se pomocí klíčového slova class. Proměnné se nazývají členská data, funkce členské metody. #include"math.h" classCParticle { doublePx, Py, Pz; // v jednotkách eV / c double M0; // v jednotkách eV / c2 doubleGetEnergy( void ) { returnsqrt( Px*Px + Py*Py + Pz*Pz + M0*M0 ); } } ; CParticle photon; photon.M0 = 0.0; photon.Px = 1.37e+9; photon.Py = 0.25e+9; photon.Pz = 1.10e+9; double Ep = photon.GetEnergy(); CParticle *electron, *positron; electron = new CParticle; positron = new CParticle; electron->M0 = pozitron->M0 = 510998.91; electron->Px = 3.11e+9; electron->Pz = 2.14e+9; double Ee = electron->GetEnergy(); Objekt je instance třídy - tj. konkrétní statická nebo dynamicky alokované proměnná typu třída. Zatímco v programu může třída daného jména být jen jedna, objektů dané třídy může být neomezený počet. Ke členským proměnným a procedurám lze přistupovat pomocí operátorů "." pro statické instance a "->" pro dynamické instance (jsou-li veřejně přístupné).

  8. Třídy zpřehledňují program double Px[100], Py[100], Pz[100]; double energy[100]; double M0[100]; for( int i = 0; i < 100; i++ ) { energy[i] = sqrt( Px[i]*Px[i] + Py[i]*Py[i] + Pz[i]*Pz[i] + M0[i]*M0[i] ); ProvedNecoSEnergii( energy[i] ); } Klasický přístup V klasickém přístupu jsou data a funkce, které s nimi pracují, zcela oddělené. Protože je na programátorovi, aby přiřadil k procedurám správná data, je zde prostor k chybám. Cparticle particles[100]; for( int i = 0; i < 100; i++ ) ProvedNecoSEnergii( particles[i].GetEnergy() ); OOP přístup OOP sdružuje data s procedurami tak, aby nemohlo dojít k mýlce, se kterými daty má procedura pracovat. Navíc není potřeba explicitně říkat, která data má procedura zpracovat – to zpřehledňuje kód a znemožňuje velkou množinu chyb. Toto je ovšem nejmenší z výhod, které OOP nabízí ...

  9. Veřejné, chráněné a soukromé … Velice důležitá vlastnost OPP je zapouzdření – tj. možnost třídy deklarovat, které z jejích členských dat a metod jsou veřejně přístupné a které nikoliv. Deklarace metod a proměnných jako neveřejné znemožní čemukoliv mimo objekt s nimi operovat – což opět znemožňuje obrovskou skupinu programátorských chyb. To je extrémně důležité zejména v případech, kdy se na jednom projektu podílí více programátorů – jako například na frameworcích experimentů částicové fyziky . Stupeň „veřejnosti“ metod a proměnných určují tři klíčová slova : public, protected a private. public – metody/proměnné jsou veřejné, lze k nim přistupovat pomocí operátorů „.“ a „->“. Tvoří rozhraní třídy. protected – metody/proměnné, které nejsou přístupné zvenčí třídy, jsou ale přístupné potomkům třídy (o dědičnosti později). private – metody/proměnné přístupné pouze zevnitř třídy a odnikud jinud. #include"math.h" classCParticle { public: doubleGetEnergy( void ) { returnsqrt( Px*Px + Py*Py + Pz*Pz + M0*M0 ); } voidSetMomentum ( doublepx, doublepy, doublepz ) { Px = px; Py = py; Pz = pz; } protected: doublePx, Py, Pz; // v jednotkách eV / c double M0; // v jednotkách eV / c2 } ;

  10. Konstruktor Každá třída může mít deklarovány speciální metody : konstruktor(y) a destruktor. Metody jsou volány v okamžiku vytvoření objektu (konstruktor), resp. dealokace objektu (destruktor). Metody slouží primárně k nastavení hodnot členských proměnných respektive k uvolnění paměti, kterou objekt během svého života alokoval. Konstruktor se musí jmenovat stejně jako třída a může mít libovolné parametry. Konstruktorů může být i více – platí pro ně stejná pravidla jako pro přetěžování normálních funkcí. #include"math.h" classCParticle { public: CParticle( void ) : Px( 0 ), Py( 0 ), Pz( 0 ), M0( 0 ) {} CParticle( doublepx, doublepy, doublepz, double m ) : Px( px ), Py( py ), Pz( pz ), M0( m ) {} doubleGetEnergy( void ) { returnsqrt( Px*Px + Py*Py + Pz*Pz + M0*M0 ); } voidSetMomentum ( doublepx, doublepy, doublepz ) { Px = px; Py = py; Pz = pz; } protected: doublePx, Py, Pz; // v jednotkách eV / c double M0; // v jednotkách eV / c2 } ; class CStack { public: CStack( void ) : alloc( 1 ), nr( 0 ) { stack = new (CParticle[1]; } CStack( unsigned space ) : alloc( space ), nr( 0 ) { stack = new CParticle[space]; } protected: CParticle*stack; // Ukazatel na dynamické pole // tvořící zásobník unsigned alloc; // Velikost zásobníku unsigned nr; // Počet částic v zásobníku } ;

  11. Destruktor Destruktor slouží k „úklidu“ po třídě. Můžou v něm být nějaké finální výpočty, výpis stavu, ladicí informace, co v něm ale být MUSÍ je dealokace dynamické paměti, kterou objekt používal. Pokud tam nebude, odkazy na takovou paměť jsou zapomenuty v okamžiku smazání objektu a paměť bude zablokována v lepším případě do konce programu, v horším do restartu počítače (memory leak). classCStack { public: CStack( void ) : alloc( 1 ), particles( 0 ) { stack = newCParticle[1]; } CStack( unsignedspace ) : alloc( space ), nr( 0 ) { stack = newCParticle[space]; } ~CStack( void ) { delete [] stack; } AddParticle( CParticleparticle ) { /*kontrola místa … */ stack[nr] = particle; nr++; } protected: CParticle*stack; // Ukazatel na dynamické pole // tvořící zásobník unsignedalloc; // Velikost zásobníku unsignednr; // Počet částic v zásobníku } ; Destruktor se jmenuje stejně jako třída, jako argument musí mít void a je uvozen znakem ~ (tilda). Třída CParticle z předchozí průsvitky destruktor mít v principu nemusí – nealokuje žádnou dynamickou paměť – a proto je možné deklaraci destruktoru úplně vynechat. Oproti tomu třída CStack alokuje paměť (dynamické pole objektů třídy CParticle), která slouží jako vlastní zásobník. Ta musí být uvolněna v okamžiku, kdy už není potřeba – tj. v destruktoru. Vypustit destruktor v tomto případě by byla hrubá chyba.

  12. Ukazatele na třídy Formálně se třída chová jako struktura (struct) a pro ukazatele na objekty tedy platí to samé, jako pro ukazatele na struktury vč. chování operátoru „->“. doubleGetRand( void ) { return( (double)rand() ) / ( (double)RAND_MAX ; } CStack *myStack = newCStack(100); // Vytváří se zásobník s kapacitou 100 částic. Na hromadě se vytvoří // objekt třídy CParticle a zavolá se jeho konstruktor. for( int i = 0; i < 100; i++ ) myStack->AddParticle( CParticle( GetRand(), GetRand(), GetRand(), 0 ) ); // Vloží do zásobníku 100 částic – volá se členská metoda /* … nějaká činnost s částicemi … */ deletemyStack; // Dealokuje objekt, na který ukazuje myStack. Při té příležitosti // zavolá destruktor objektu. Objekt třídy CStack je alokován dynamicky na hromadě. Oproti tomu objekt typu CParticle, který je předáván metodě AddParticle, je lokální, tj. je vytvořen na zásobníku (všimněte si, že je nepojmenovaný). Není to chyba – nezmizí všechny částice po opuštění současné procedury? Není. Objekt třídy CParticle se metodě předává hodnotou – tj. se zkopíruje. Pozn.: Kopíruje se ovšem dvakrát – jednou při předávání do argumentu metody a podruhé v těle metody (viz předchozí slide). Tento postup může být u složitějších objektů neekonomický a navíc je často třeba napsat vlastní kopírovací konstruktor.

  13. Kopírovací konstruktor Předávat hodnotou základní typy je snadné. Předávat hodnotou objekty (resp. je přiřazovat) může být naopak velice netriviální, pokud obsahují odkazy na dynamickou paměť, další objekty nebo obecně ukazatele. Problém je zjevný z následujícího obrázku: Standardně se objekty kopírují jako struktury člen po členu (překopírují se doslova obsahy členských proměnných). Obsahuje-li objekt ukazatele, zko-pírují se obsahy ukazatelů (adresy), nikoliv hodnoty, na které ukazují. To způsobí, že objekt a kopie objektu mají společná data. To sice může být někdy záměr, ale daleko častěji je to nežádoucí, neboť změna v objektu znamená, že se okamžitě změní i kopie (a vice versa), a navíc pokud data dealokuje objekt, z kopie zmizí také (a druhá dea-lokace těchto dat v kopii způsobí segmentation violation chybu). Objekt Kopie člen po členu Kopie objektu Mělká kopie HROMADA

  14. Kopírovací konstruktor Předávat hodnotou základní typy je snadné. Předávat hodnotou objekty (resp. je přiřazovat) může být naopak velice netriviální, pokud obsahují odkazy na dynamickou paměť, další objekty nebo obecně ukazatele. Problém je zjevný z následujícího obrázku: Aby vznikla plnohodnotná (deep) kopie, je třeba vyrobit i kopii alokovaných dat a příslušné ukazatele naplnit adresami těchto kopií. To ovšem za programátora překladač neudělá a příslušný algoritmus si musí napsat sám. Kopírovací konstruktor má následu-jící syntax: Objekt Kopie člen po členu třída( třída &copy ) {…} Kopie objektu Pro všechny přiřazování objektu používá překladač automaticky tento konstruktor, je-li přítomen. Hluboká kopie HROMADA

  15. Kopírovací konstruktor Předávat hodnotou základní typy je snadné. Předávat hodnotou objekty (resp. je přiřazovat) může být naopak velice netriviální, pokud obsahují odkazy na dynamickou paměť, další objekty nebo obecně ukazatele. Modifikace třídy CStack s kopírovacím konstruktorem: Všimněte si použití operátoru sizeof, který vrátí velikost zadaného typu v byte, např. sizeof(char) = 1 sizeof(short) = 2 sizeof(double) = 8 sizeof(CParticle) = 32 Dále si všimněte použití funkce memcpy : classCStack { public: … CStack( CStack & copy ) { alloc = copy.alloc; nr = copy.nr; stack = newCParticle[alloc]; memcpy( stack, copy.stack, sizeof(CParticle) * alloc ); } /*CStack*/ protected: CParticle*stack; // Ukazatel na dynamické pole // tvořící zásobník unsignedalloc; // Velikost zásobníku unsignednr; // Počet částic v zásobníku } ; /*CStack*/ memcpy( void *to, const void *from, size_t count ); Funkce zkopíruje část paměti do jiné části paměti (oblasti se nesmí překrývat) po jednotlivých byte. Začátky paměťových oblastí popisují ukazatele to a from, počet byte parametr count. Typ size_t je celo-číselný popisující velikosti paměti (obvykle je to alias pro int). Existují další podobné funkce (memmove, strcpy a další).

  16. Přetěžování operátorů Specialitou C++ je přetěžování operátorů. Význam operátorů je definován pro základní typy, je ale možné tento význam rozšířit tak, aby platil i pro třídy nebo jiné složené typy. Například : V rámečku vlevo je definována třída complexp, reprezentující komplexní číslo v polárních souřadnicích. Všim-něte si chráněných hodnot, ke kterým je přístup pouze pomocí veřejných člen-ských metod, které v sobě mají zabu-dovanou ochranu před zadání nesmy-slných čísel. Complexp je relativně složitá konstruk-ce, chceme ale, aby se chovala jako číslo, tedy aby byly možné příkazy typu #define PI 3.1415926535897 #define PI2 2*PI classcomplexp { public: complexp( void ) : fArg(0), fPhi(0) {} complexp( doublearg ) : fPhi( 0 ) { Arg( arg ); } complexp( doublearg, doublephi ) { Arg( arg ); Phi( phi ); } doubleArg( void ) { returnfArg; } doublePhi( void ) { returnfPhi; } voidArg( doublearg ) { if(arg < 0 ) arg *= -1; fArg = arg; } voidPhi( doublephi ) { while( phi >= PI2 ) phi -= PI2; while( phi < 0 ) phi += PI2; fPhi = phi; } protected: doublefArg; doublefPhi; } ; /* complexp */ int a = 5; int b = 6 int aplusb = a + b; complexp A( 1, PI ); complexp B( 5, 3*PI/2 ); complexp AplusB = A + B; Pozn. : destruktor a kopírovací konstruktor jsou pro tuto třídu zbytečné, neboť nealokuje žádnou paměť a neobsahuje žádné ukazatele.

  17. Přetěžování operátorů Operátor lze „přetížit“ podobně jako funkci – a jako funkce se také příslušný algoritmus zapisuje. Sčítání například přidáme k complexp následovně : Jméno „funkce“ operátoru je vždy určeno klíčovým slovem operator a pak následuje patřičný symbol. Binární operátory pak mají jeden parametr – a tím je druhý operand. První operand je vždy „tento“ objekt. Argument přetíženého operátoru může být cokoliv, takže klidně můžeme definovat operátor, který sčítá komplex-ní a reálné číslo, nebo třeba komplexní číslo a bramboru. Pozn.: operátory lze definovat i mimo třídy (ne jako členské, nýbrž normální funkce). Pak by deklarace sčítání z příkladu nalevo vypadala takto: #define PI 3.1415926535897 #define PI2 2*PI classcomplexp { public: … complexpoperator+( complexp & cislo ) { double re = fArg * cos( fPhi ) + cislo.fArg * cos( cislo.fPhi ); doubleim = fArg * sin( fPhi ) + cislo.fArg * sin( cislo.fPhi ); doublearg = sqrt( re*re + im*im ); doublephi = acos( re / arg ); returncomplexp( arg, phi ); } /*operator+*/ … protected: doublefArg; doublefPhi; } ; /*complexp*/ complexp operator+(complexp & cislo1, complexp & cislo2 ) CVIČĚNÍ : dopište zbylou aritmetiku pro complexp

  18. Statické členy Třídy mohou obsahovat metody, které nepracují s žádnými členskými daty. V takovém případě je dobré je označit jako statické (klíčové slovo static) – tím je zajištěno, že budou přístupné i bez toho, aby byl instanciován nějaký objekt dané třídy. K přístupu k takovým metodám použijeme jméno třídy spolu s operátorem „::“ . classCBremsstrahlung { … staticdoubleGammaDiffCrossSection( double E0, doubleTheta, doubleEnergy ) { doubleErel = (E0 - Energy)/E0; double Elin = E0*(E0 - Energy); doublescr = screen_const * Energy / Elin; double emp1, emp2, bremss emp1 = 16.863 - 2*log( 1 + 0.311877*scr*scr ) + 2.4*exp(-0.9*scr) + 1.6*exp(-1.5*scr); emp2 = emp1 - 0.666666 / ( 1 + 6.5*scr + 6*scr*scr ); bremss = (1 + Erel * Erel)*(0.25*emp1 - b_const) - 0.666666*Erel*(0.25*emp2 - b_const); if( Theta < 0.0 ) Theta *= -1; bremss *= exp( - Theta ); returnbremss; } /* GammaDiffCrossSection */ }; double brm = CBremsstrahlung::GammaDiffCrossSection( 1e9, PI/3, 1e8 );

  19. Dědičnost Pod tímto pojmem se skrývá asi ta největší zbraň OOP. Hlavní myšlenka dědičnosti je znovupoužitelnost, to znamená, že můžeme vytvářet nové třídy založené na třídě, která již byla definována, místo toho abychom museli znovu psát již jednou napsaný kód jen s jinými typy proměnných. Díky dědičnosti je možné napsat kód jednou pro obecnější typ a poté ho používat pro všechny jeho potomky. Potmoci od svého rodiče dědí veškerou jeho funkčnost a rozhraní (označené jako public nebo protected). To umožňuje v rodičovské třídě deklarovat vlastnosti, data a procedury, které jsou pro všechny potomky společné. Např. každá částice má hybnost, polohu, klidovou hmotnost a energii a vztahy mezi těmito veličinami jsou pro všechny částice stejné. Co se liší jsou například interakce, kterými různé druhy částic prochází – tj. metody pracující s interakcemi musí mít každý potomek napsané zvlášť. CElectron CParticle CPositron CPhoton Rodič Potomci

  20. Dědičnost Pod tímto pojmem se skrývá asi ta největší zbraň OOP. Hlavní myšlenka dědičnosti je znovupoužitelnost, to znamená, že můžeme vytvářet nové třídy založené na třídě, která již byla definována, místo toho abychom museli znovu psát již jednou napsaný kód jen s jinými typy proměnných. Díky dědičnosti je možné napsat kód jednou pro obecnější typ a poté ho používat pro všechny jeho potomky. Stejně tak samotné interakce (popsané třídami) mohou mít společný základ, vlastní algoritmus, který interakci provádí, musí mít ale každý typ interakce svůj. CCompton CInteraction CPairProduction CPhoefect Rodič Potomci

  21. Dědičnost classCInteraction { public: … voidAddTotalEnergy( double val ) { gTotalEnergy += val; }; // Přidá celkovou uloženou energii (po aplikování cutu). gTotalEnergy je globální proměnná. voidSetStackPointer( CStack *pt ) { StackPointer = pt; }; // Nastaví ukazatel na zásobník voidSetPrimaryParticlePointer( CParticle *pt ) {ParticlePointer = pt; }; // Nastaví ukazatel na zásobník voidCountSecondaryMomentum( CParticle *secondaryPt, doubleprecession_angle, doublerotation_angle, doublemomentum_length ); // Nastaví absolutní hybnost sekundární částice, je-li známa hybnost primární částice // a relativní hybnost (precese, rotace a délka) protected: CStack *StackPointer; // Ukazatel na zásobník CParticle *PParticlePointer; // Ukazatel na částici, které prochází interakcí }; /* CInteraction */ Nahoře je naznačená část třídy CInteraction, která tvoří základ (je rodič) všech tříd popisujících interakce částic spršky s látkou. Zavádí proměnné a metody společné pro všechny interakce (jakési „podhoubí“).

  22. Dědičnost classCPairProduction : publicCInteraction { public: doublePairDiffCrossSection( doubleTheta_positron, doubleEnergy_positron, doubleTheta_electron, doubleEnergy_electron ); // Vrací diferenciální účinný průřez pro dané úhly a energie produktů intGetPairProd( double *Theta_p, double *Energy_p, double *Theta_e, double *Energy_e, doublelow_theta, doublehigh_theta, doublelow_energy, doublehigh_energy ); // Vytvoří e-e pár dle distribuce dané PaidRiffCrossSection v daném rozsahu. // První čtyři argumenty jsou návratové hodnoty – energie a relativní úhly elektronu // a pozitronu. Vrací počet náhodných vektorů, které musely být vygenerovány, aby // pár vznikl (pro statistické účely). }; /*CRPairProduction*/ Takto deklarovaná třída má k dispozici všechno, co třída CInteraction a navíc metody deklarované zde. Tj. lze použít … CPairProduction *interaction = new CPairProduction; interaction -> SetStackPointer( stack ); interaction -> SetPrimaryParticle( paricle ); double pdcs = interaction -> PairDiffCrossSection( PI/3, 1e9, PI/5, 1e10 );

  23. Polymorfizmus Samotná dědičnost je velice šikovná, šetří práci a dovoluje dělit program na logické celky, co z ní ale dělá opravdu mocný nástroj, je polymorfizmus. Polymorfizmus dovoluje dvě věci – automatické přetypování třídy či ukazatele na ni na typ svého rodiče a deklaraci virtuálních metod. S každým objektem se dá pracovat stejně, jako by se pracovalo s objektem rodičovské třídy. Do ukazatele na rodiče lze uložit ukazatel na potomka a pak s ním zcela normálně pracovat. To je extrémně užitečné ve spojení s virtuálními metodami. CStack *stack = newCStack(100); CPairProduction *pair = newCPairProduction; CInteraction *someInt = (CInteraction *)pair; pair -> SetStackPointer( stack ); someInt -> SetStackPointer( stack ); // Tyto dva příkazy jsou zcela ekvivalentní classCInteraction { public: … virtualdoubleGetTotalCrossSection( void ) { return 0; } // Vrátí celkový účinný průřez pro interakci virtualintExec( void ) { return 0; } // Provede interakci pro danou částici virtualchar *Identify( void ) { return“CInteraction“; } // Vrátí jméno interakce }; /* CInteraction */ Virtuální metodu deklarovanou v rodiči může libovolný potomek předefinovat dle svých potřeb. Je-li tato metoda zavolána pomocí operátoru „->“, vždy se provede ta, která náleží k danému objektu, i když ukazatel má typ rodiče. To umožňuje například v tomto případě provádět libovolnou interakci vráce-nou libovolnou částicí na zásobníku aniž by během překladu bylo nutné zjišťovat, která tam vlastně je.

  24. Virtuální členské funkce classCInteraction { public: … virtualdoubleGetTotalCrossSection( void ) { return 0; } // Vrátí celkový účinný průřez pro interakci virtualintExec( void ) { return 0; } // Provede interakci pro danou částici virtualchar *Identify( void ) { return“CInteraction“; } // Vrátí jméno interakce }; /* CInteraction */ classCPairProduction : publicCInteraction { public: … virtualchar *Identify( void ) { return“Pair Production “; } // Vrátí jméno interakce }; /* CPairProduction */ classCCompton : publicCInteraction { public: … virtualchar *Identify( void ) { return“Compton“; } // Vrátí jméno interakce }; /* CPairProduction */ Tři potomci třídy CInteraction předefinovávají virtuální metodu Identify, která vrací řetězec identifikující druh interakce. classCPhotoefect : publicCInteraction { public: … virtualchar *Identify( void ) { return“Photoefect“; } // Vrátí jméno interakce }; /* CPhotoefect */

  25. Virtuální členské funkce classCInteraction { public: … virtualdoubleGetTotalCrossSection( void ) { return 0; } // Vrátí celkový účinný průřez pro interakci virtualintExec( void ) { return 0; } // Provede interakci pro danou částici virtualchar *Identify( void ) { return“CInteraction“; } // Vrátí jméno interakce }; /* CInteraction */ classCPairProduction : publicCInteraction { public: … virtualchar *Identify( void ) { return“Pair Production “; } // Vrátí jméno interakce }; /* CPairProduction */ classCCompton : publicCInteraction { public: … virtualchar *Identify( void ) { return“Compton“; } // Vrátí jméno interakce }; /* CPairProduction */ Interakce : Pair production Interakce : Compton Interakce : Photoefect CPairProduction *pprod = new CPairProduction; CCompton *compt = new CCompton; CPhotoefect *photo = new CPhotoefect; CInteraction *interact; interact = (CInteraction *) pprod; printf( “Interakce : %s\n“, interact -> Identify() ); interact = (CInteraction *) compt; printf( “Interakce : %s\n“, interact -> Identify() ); interact = (CInteraction *) photo ; printf( “Interakce : %s\n“, interact -> Identify() ); classCPhotoefect : publicCInteraction { public: … virtualchar *Identify( void ) { return“Photoefect“; } // Vrátí jméno interakce }; /* CPhotoefect */

  26. Virtuální členské funkce CStack *stack = new CStack(10000); stack->AddParticle( CPhoton( 1e9, 0, 0 ) ); … while( ! stack->IsEmpty() ) { CParticle *particle = stack->GetTopmostParticle(); printf( “Castice : %s\n“, particle -> Identify() ); CInteraction *interaction = particle->GetInteraction(); printf( “Interakce : %s\n“, interact -> Identify() ); interaction->Exec(); } … V bílém rámečku je příklad, jak by mohla vypadat hlavní smyčka simulace. Ze zásobníku je vyjmuta první částice. Ta vrátí náhodně jednu z interakcí, kterou může projít (dle nastavených rozdělení hustot pravdě-podobnosti) pomocí virtuální metody GetInteraction() - každá z částic vrací jiné interakce podle jiných rozdělení. V cyklu je zavolána virtuální metoda Exec(), která provede interakci a do zásobníku vloží nové sekundární částice (nebo je zahodí, pokud mají energii pod nějakou minimální hodnotou). Tak cykl pokračuje, dokud zásobník není prázdný (všechny zbylé částice předaly svou energii okolní hmotě). Castice: Photon Interakce : Compton Castice: Positron Interakce : Bremsstrahlung Castice: Electron Interakce : Hard Scattering Castice: Photon Interakce : Photoeffect …

  27. Čistě abstraktní třídy Je možné napsat čistě abstraktní třídu – takovou, která obsahuje deklarace virtuálních metod, které ovšem nejsou implementovány – čistě virtuální metody. Takové třídy pak nelze instanciovat (nelze vytvořit objekt této třídy), lze je ale použít jako rodiče pro jiné třídy, které musí čistě virtuální metody předefinovat. Jinými slovy, čistě abstraktní třídy slouží jako rozhraní, zajišťující, že dědické třídy budou mít shodnou funkčnost. classCInteraction { public: … virtualdoubleGetTotalCrossSection( void ) { return 0; } // Vrátí celkový účinný průřez pro interakci virtualintExec( void ) { return 0; } // Provede interakci pro danou částici virtualchar *Identify( void ) { return“CInteraction“; } // Vrátí jméno interakce }; /* CInteraction */ classCInteraction { public: … virtualdoubleGetTotalCrossSection( void ) = 0; // Vrátí celkový účinný průřez pro interakci virtualintExec( void ) = 0; // Provede interakci pro danou částici virtualchar *Identify( void ) = 0; // Vrátí jméno interakce }; /* CInteraction */ Nalevo základová třída CInteraction, která má virtuální metody implementovány (byť nic nedělající). Napravo ta samá třída jako čistě abstraktní, která definuje rozhraní společné pro všechny interakce.

  28. Poznámka o šablonách Mohou existovat skupiny tříd, které implementují stejný algoritmus pro různé datové typy. Například třída komplex může uchovávat čísla v jednoduché, dvojité či čtyřnásobné přesnosti. Psát ovšem třikrát to samé jen s jinými typy proměnných je otravné. V C++ je možné si život zjednodušit pomocí šablon: classcomplexf { … complex( floatre, floatim) : fRe(re), fIm(im) {} … floatfRe; floatfIm; } ; /* complexf */ classcomplexd { … complex( double re, doubleim) : fRe(re), fIm(im) {} … double fRe; double fIm; } ; /* complexd */ classcomplexld { … complex( longdouble re, longdoubleim) : fRe(re), fIm(im) {} … longdouble fRe; longdouble fIm; } ; /* complexld */ Ve standardních knihovnách (STL) je mnoho algoritmů řešeno šablonami. Pozor! Šablony se překládají pouze tehdy, jsou-li opravdu potřeba, tj. někdy v nich mohou zůstat syntaktické chyby, kterých si překladač nevšimne hned. Typ T je při deklaraci nahrazen nějakým konkrétním typem, např: complex<float> A, B; complex<double> C; template <classT> classcomplex { complex( T re, T im) : fRe(re), fIm(im) {} T fRe; T fIm; } ; /* complex */

  29. Standardní knihovna šablon (STL) • STL (Standard Template Library) je knihovna shrnující nejčastěji používané algoritmy a funkce. její vývoj začal roku 1992. Většinu jí napsal A. Stěpanov, který byl spolupracovníkem B. Stroustrupa – toho také přesvědčil, aby do C++ zavedl generické typy jako měla tou dobou Ada (šablony). Je zajímavé, že Stěpanov se na OOP samotné divá celkem kriticky. • STL obsahuje například: • Kontejnery • Dynamická pole (vektory) • Spojové seznamy • Mapy a „množiny“ (sets) • Fronty a zásobníky • Algoritmy • Třídění • Vyhledávání • Cykly typu „foreach“ • Iterátory (unifikovaný přístup k prvkům kontejnerů) • Objektové řetězce a práce s nimi • Třídy pro objektovou práci se soubory Alexandr Stěpanov 1950 -

  30. Dynamické pole vector STL Třída (šablona) vektor implementuje základní funkčnost dynamického pole – umožňuje alokovat místo na určitý počet proměnných či objektů a svoji velikost operativně upravuje podle požadavků programátora. Přetypovaný operátor „[]“ pak umožňuje přístup k prvkům stejný, jako u standardních polí. Příklady použití třídy vector. vector<complex> complexArray; complexArray.reserve(100); complexArray[10] = complex(3,5); complexArray[99] = complex(1,1); complexArray[1000] = complex(-1,1); // Chyba! fprintf( “kapacita : %i\n“, complexArray.capacity() ); for( int i = 0; i < 100; int ++ ) complexArray.push_back( complex( i, -i ) );

  31. Iterátory Iterátory tvoří kodifikovaná rozhraní přístupu ke všem kontejnerů STL – a samozřejmě je doporučené toto rozhraní zachovat i v uživatelských třídách. Iterátor je ukazatel na typ, který kontejner uchovává. Metoda begin() vrací iterátor na první prvek, metoda end() vrací ukazatel ZA poslední prvek. Použití je následující: classNtuple { public: … // STL iterator typedefdouble* iterator; // STL konstantní iterator typedefconstdouble* const_iterator; // Vrátí počátek iterací iteratorbegin() { returnfElementsArray; } // Vrátí počátek iterací (konstantní iterátor) const_iteratorbegin() const { returnfElementsArray; } // Vrátí konec iterací iteratorend() { returnfElementsArray + fDimension; } // Vrátí konec iterací (konstantní iterátor) const_iteratorend() const { returnfElementsArray + fDimension; } private : intfDimension; double *fElementsArray }; Ntuple myNtuple; … Ntuple::iterator myIter = myNtuple.begin(); for( ; myIter < myNtuple.end(); ++myIter ) printf( “%f “, *myIter ) S iterátory se pracuje vždy stejně, i když ukazují na mnohem komplikovanější ob-jekty.

  32. Třídění, vyhledávání a další algoritmy boolbinary_search( forward_iterator start, forward_iteratorend, const TYPE& val ); void sort( random_iterator start, random_iteratorend ); void sort_heap (random_access_iterator start, random_access_iteratorend) boolnext_permutation( bidirectional_iterator start, bidirectional_iteratorend ); boolprev_permutation( bidirectional_iterator start, bidirectional_iteratorend ); V rámečku nahoře jsou příklady některých algoritmů, které jsou implementovány v STL. Jsou psány vesměs tak, aby byly aplikovatelné na libovolné kontejnery a tedy jako své argumenty požadují iterátory.

More Related