Prezentace se nahrává, počkejte prosím

Prezentace se nahrává, počkejte prosím

Jazyk C++ Bjarne Stroustrup 1950 - 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ý.

Podobné prezentace


Prezentace na téma: "Jazyk C++ Bjarne Stroustrup 1950 - 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ý."— Transkript prezentace:

1 Jazyk C++ Bjarne Stroustrup 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). Pro projekt „Cesta k vědě“ (veda.gymjs.net) vytvořil V. Pospíšil Modifikace a šíření dokumentu podléhá licenci CC-BY-SA. Evropský sociální fond Praha & EU: Investujeme do vaší budoucnosti

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. Příklad EM spršky v mlžné komoře iniciované elektronem kosmického záření v mg. poli (1959) 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. 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. Fotonové interakce 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 m e c 2 je přeměněna na kinetickou energii produktů. 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. 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.

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. Primární foton TP PHT CR BZ PHT BZ AN TR BZ TR BZ Foton Elektron Pozitron PHT Fotoefekt TP Tvorba párů BZ Brzdné záření TR Tvrdý rozptyl AN Anihilace CR Compton

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. 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. #include "math.h" class CParticle { double Px, Py, Pz; // v jednotkách eV / c double M0; // v jednotkách eV / c 2 double GetEnergy( void ) { return sqrt( 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 = ; electron->Px = 3.11e+9; electron->Pz = 2.14e+9; double Ee = electron->GetEnergy(); 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] ); } Cparticle particles[100]; for( int i = 0; i < 100; i++ ) ProvedNecoSEnergii( particles[i].GetEnergy() ); Klasický přístup OOP 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. 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. #include "math.h" class CParticle { public: double GetEnergy( void ) { return sqrt( Px*Px + Py*Py + Pz*Pz + M0*M0 ); } void SetMomentum ( double px, double py, double pz ) { Px = px; Py = py; Pz = pz; } protected: double Px, Py, Pz; // v jednotkách eV / c double M0; // v jednotkách eV / c 2 } ; 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.

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. #include "math.h" class CParticle { public: CParticle( void ) : Px( 0 ), Py( 0 ), Pz( 0 ), M0( 0 ) {} CParticle( double px, double py, double pz, double m ) : Px( px ), Py( py ), Pz( pz ), M0( m ) {} double GetEnergy( void ) { return sqrt( Px*Px + Py*Py + Pz*Pz + M0*M0 ); } void SetMomentum ( double px, double py, double pz ) { Px = px; Py = py; Pz = pz; } protected: double Px, Py, Pz; // v jednotkách eV / c double M0; // v jednotkách eV / c 2 } ; 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í. 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). class CStack { public: CStack( void ) : alloc( 1 ), particles( 0 ) { stack = new CParticle[1]; } CStack( unsigned space ) : alloc( space ), nr( 0 ) { stack = new CParticle[space]; } ~CStack( void ) { delete [] stack; } AddParticle( CParticle particle ) { /*kontrola místa … */ stack[nr] = particle; nr++; } 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 } ; 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 „->“. double GetRand( void ) { return ( ( double )rand() ) / ( ( double )RAND_MAX ; } CStack *myStack = new CStack(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 … */ delete myStack; // 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: HROMADA Objekt Kopie člen po členu Kopie objektu 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). Mělká kopie

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: HROMADA Objekt Kopie člen po členu Kopie objektu 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: Hluboká kopie třída( třída © ) {…} Pro všechny přiřazování objektu používá překladač automaticky tento konstruktor, je-li přítomen.

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: class CStack { public: … CStack( CStack & copy ) { alloc = copy.alloc; nr = copy.nr; stack = new CParticle[alloc]; memcpy( stack, copy.stack, sizeof(CParticle) * alloc ); } /*CStack*/ 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 } ; /*CStack*/ 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 : 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 : #define PI #define PI2 2*PI class complexp { public: complexp( void ) : fArg(0), fPhi(0) {} complexp( double arg ) : fPhi( 0 ) { Arg( arg ); } complexp( double arg, double phi ) { Arg( arg ); Phi( phi ); } double Arg( void ) { return fArg; } double Phi( void ) { return fPhi; } void Arg( double arg ) { if (arg < 0 ) arg *= -1; fArg = arg; } void Phi( double phi ) { while( phi >= PI2 ) phi -= PI2; while( phi < 0 ) phi += PI2; fPhi = phi; } protected: double fArg; double fPhi; } ; /* complexp */ 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 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ě : #define PI #define PI2 2*PI class complexp { public: … complexp operator+( complexp & cislo ) { double re = fArg * cos( fPhi ) + cislo.fArg * cos( cislo.fPhi ); double im = fArg * sin( fPhi ) + cislo.fArg * sin( cislo.fPhi ); double arg = sqrt( re*re + im*im ); double phi = acos( re / arg ); return complexp( arg, phi ); } /*operator+*/ … protected: double fArg; double fPhi; } ; /*complexp*/ 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: 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 „::“. class CBremsstrahlung { … static double GammaDiffCrossSection( double E0, double Theta, double Energy ) { double Erel = (E0 - Energy)/E0; double Elin = E0*(E0 - Energy); double scr = screen_const * Energy / Elin; double emp1, emp2, bremss emp1 = *log( *scr*scr ) + 2.4*exp(-0.9*scr) + 1.6*exp(-1.5*scr); emp2 = emp / ( *scr + 6*scr*scr ); bremss = (1 + Erel * Erel)*(0.25*emp1 - b_const) *Erel*(0.25*emp2 - b_const); if( Theta < 0.0 ) Theta *= -1; bremss *= exp( - Theta ); return bremss; } /* 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. CParticle CElectron CPositron CPhoton RodičPotomci 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ášť.

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. CInteraction CCompton CPairProduction CPhoefect RodičPotomci 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.

21 Dědičnost class CInteraction { public: … void AddTotalEnergy( double val ) { gTotalEnergy += val; }; // Přidá celkovou uloženou energii (po aplikování cutu). gTotalEnergy je globální proměnná. void SetStackPointer( CStack *pt ) { StackPointer = pt; }; // Nastaví ukazatel na zásobník void SetPrimaryParticlePointer( CParticle *pt ) {ParticlePointer = pt; }; // Nastaví ukazatel na zásobník void CountSecondaryMomentum( CParticle *secondaryPt, double precession_angle, double rotation_angle, double momentum_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 class CPairProduction : public CInteraction { public: double PairDiffCrossSection( double Theta_positron, double Energy_positron, double Theta_electron, double Energy_electron ); // Vrací diferenciální účinný průřez pro dané úhly a energie produktů int GetPairProd( double *Theta_p, double *Energy_p, double *Theta_e, double *Energy_e, double low_theta, double high_theta, double low_energy, double high_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. CStack *stack = new CStack(100); CPairProduction *pair = new CPairProduction; CInteraction *someInt = (CInteraction *)pair; pair -> SetStackPointer( stack ); someInt -> SetStackPointer( stack ); // Tyto dva příkazy jsou zcela ekvivalentní 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. class CInteraction { public: … virtual double GetTotalCrossSection( void ) { return 0; } // Vrátí celkový účinný průřez pro interakci virtual int Exec( void ) { return 0; } // Provede interakci pro danou částici virtual char *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 class CInteraction { public: … virtual double GetTotalCrossSection( void ) { return 0; } // Vrátí celkový účinný průřez pro interakci virtual int Exec( void ) { return 0; } // Provede interakci pro danou částici virtual char *Identify( void ) { return “CInteraction“; } // Vrátí jméno interakce }; /* CInteraction */ class CPairProduction : public CInteraction { public: … virtual char *Identify( void ) { return “Pair Production “; } // Vrátí jméno interakce }; /* CPairProduction */ class CCompton : public CInteraction { public: … virtual char *Identify( void ) { return “Compton“; } // Vrátí jméno interakce }; /* CPairProduction */ class CPhotoefect : public CInteraction { public: … virtual char *Identify( void ) { return “Photoefect“; } // Vrátí jméno interakce }; /* CPhotoefect */ Tři potomci třídy CInteraction předefinovávají virtuální metodu Identify, která vrací řetězec identifikující druh interakce.

25 Virtuální členské funkce class CInteraction { public: … virtual double GetTotalCrossSection( void ) { return 0; } // Vrátí celkový účinný průřez pro interakci virtual int Exec( void ) { return 0; } // Provede interakci pro danou částici virtual char *Identify( void ) { return “CInteraction“; } // Vrátí jméno interakce }; /* CInteraction */ class CPairProduction : public CInteraction { public: … virtual char *Identify( void ) { return “Pair Production “; } // Vrátí jméno interakce }; /* CPairProduction */ class CCompton : public CInteraction { public: … virtual char *Identify( void ) { return “Compton“; } // Vrátí jméno interakce }; /* CPairProduction */ class CPhotoefect : public CInteraction { public: … virtual char *Identify( void ) { return “Photoefect“; } // Vrátí jméno interakce }; /* CPhotoefect */ 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() ); Interakce : Pair production Interakce : Compton Interakce : Photoefect

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. class CInteraction { public: … virtual double GetTotalCrossSection( void ) = 0; // Vrátí celkový účinný průřez pro interakci virtual int Exec( void ) = 0; // Provede interakci pro danou částici virtual char *Identify( void ) = 0; // Vrátí jméno interakce }; /* CInteraction */ class CInteraction { public: … virtual double GetTotalCrossSection( void ) { return 0; } // Vrátí celkový účinný průřez pro interakci virtual int Exec( void ) { return 0; } // Provede interakci pro danou částici virtual char *Identify( void ) { return “CInteraction“; } // 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: class complexd { … complex( double re, double im) : fRe(re), fIm(im) {} … double fRe; double fIm; } ; /* complexd */ class complexf { … complex( float re, float im) : fRe(re), fIm(im) {} … float fRe; float fIm; } ; /* complexf */ class complexld { … complex( long double re, long double im) : fRe(re), fIm(im) {} … long double fRe; long double fIm; } ; /* complexld */ template class complex { complex( T re, T im) : fRe(re), fIm(im) {} T fRe; T fIm; } ; /* complex */ Typ T je při deklaraci nahrazen nějakým konkrétním typem, např: complex A, B; complex C; 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.

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 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

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í. vector 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 ) ); Příklady použití třídy vector.

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í: class Ntuple { public: … // STL iterator typedef double* iterator; // STL konstantní iterator typedef const double* const_iterator; // Vrátí počátek iterací iterator begin() { return fElementsArray; } // Vrátí počátek iterací (konstantní iterátor) const_iterator begin() const { return fElementsArray; } // Vrátí konec iterací iterator end() { return fElementsArray + fDimension; } // Vrátí konec iterací (konstantní iterátor) const_iterator end() const { return fElementsArray + fDimension; } private : int fDimension; 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 bool binary_search( forward_iterator start, forward_iterator end, const TYPE& val ); void sort( random_iterator start, random_iterator end ); void sort_heap (random_access_iterator start, random_access_iterator end) bool next_permutation( bidirectional_iterator start, bidirectional_iterator end ); bool prev_permutation( bidirectional_iterator start, bidirectional_iterator end ); 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.


Stáhnout ppt "Jazyk C++ Bjarne Stroustrup 1950 - 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ý."

Podobné prezentace


Reklamy Google