mp; from) { // kopirovanie po chlenam X } R.13.4.4 Vyzov funkcii Vyzov funkcii est' konstrukciya vida: pervichnoe-vyrazhenie ( spisok-vyrazhenij opt ) Ona schitaetsya binarnoj operaciej, v kotoroj pervichnoe-vyrazhenie predstavlyaet pervyj operand, a spisok-vyrazhenij (vozmozhno pustoj), - vtoroj operand. Imenem, zadayushchim funkciyu, sluzhit operator(), i vyzov x(arg1,arg2,arg3) dlya ob容kta klassa x interpretiruetsya kak x.operator()(arg1,arg2,arg3). Funkciya operator() dolzhna byt' nestaticheskoj funkciej-chlenom klassa x. R.13.4.5 Indeksaciya Indeksaciya, opredelyaemaya kak: pervichnoe-vyrazhenie [ vyrazhenie ] schitaetsya binarnoj operaciej. Vyrazhenie s indeksaciej x[y] dlya ob容kta klassa x interpretiruetsya kak x.operator[](y). Funkciya operator[] dolzhna byt' nestaticheskoj funkciej-chlenom klassa x. R.13.4.6 Dostup k chlenu klassa Dostup k chlenu klassa opredelyaetsya s pomoshch'yu operacii ->: pervichnoe-vyrazhenie -> pervichnoe-vyrazhenie On schitaetsya unarnoj operaciej. Dlya ob容kta klassa x vyrazhenie x->m interpretiruetsya kak (x.operator->())->m. Otsyuda sleduet, chto funkciya operator->() dolzhna vozvrashchat' ili ukazatel' na klass, ili ssylku na klass, ili ob容kt klassa, dlya kotorogo opredelena funkciya operator->(). Ona dolzhna byt' nestaticheskoj funkciej-chlenom klassa. R.13.4.7 Inkrement i dekrement Funkciya s imenem operator++ i s odnim parametrom zadaet dlya ob容ktov nekotorogo klassa operaciyu prefiksnogo inkrementa ++. Funkciya s imenem operator++ i s dvumya parametrami zadaet dlya ob容ktov nekotorogo klassa operaciyu postfiksnogo inkrementa ++. Dlya postfiksnoj operacii ++ vtoroj parametr dolzhen byt' tipa int, i, kogda v vyrazhenii vstrechaetsya operaciya postfiksnogo inkrementa, funkciya operator++ vyzyvaetsya so vtorym parametrom, ravnym nulyu. Privedem primer: class X { public: X operator++(); // prefiksnaya ++a X operator++(int) // postfiksnaya a++ }; void f(X a) { ++a; // a.operator++(); a++; // a.operator++(0); a.operator++(); // yavnyj vyzov: dejstvuet kak ++a; a.operator++(0); // yavnyj vyzov: dejstvuet kak a++; } Prefiksnye i postfiksnye operacii dekrementa -- opredelyayutsya analogichnym obrazom. R.14 SHABLONY TIPA R.14.1 SHablony tipa SHablon tipa opredelyaet celoe semejstvo tipov ili funkcij. opisanie-shablona-tipa: template < spisok-parametrov-shablona-tipa> opisanie spisok-parametrov-shablona-tipa: parametr-shablona-tipa spisok-parametrov-shablona-tipa , parametr-shablona-tipa parametr-shablona-tipa: parametr-tipa opisanie-parametra parametr-tipa: class identifikator Konstrukciya opisanie v opisanii-shablona-tipa dolzhna soderzhat' opisanie ili opredelenie funkcii ili klassa. V konstrukcii parametr-tipa identifikator opredelyaetsya kak imya-tipa v oblasti vidimosti opisaniya shablona tipa. Imena shablonov tipa podchinyayutsya obychnym pravilam dlya oblastej vidimosti i kontrolya dostupa. Konstrukciya opisanie-shablona-tipa schitaetsya opisaniem. Ona mozhet prisutstvovat' v programme tol'ko kak global'noe opisanie. R.14.2 SHablony tipov dlya klassov SHablon tipa dlya klassa opredelyaet kak budut stroit'sya klassy, podobno tomu, kak opisanie klassa opredelyaet kak budut stroit'sya ob容kty etogo klassa. SHablon tipa dlya klassa vector mozhno opisat' sleduyushchim obrazom: template<class T> class vector { T* v; int sz; public: vector(int); T& operator[](int); T& elem(int i) { return v[i] } // ... }; Prefiks template<class T> pokazyvaet, chto opisyvaetsya shablon tipa, i chto v etom opisanii ispol'zuetsya imya-tipa T, inymi slovami, vector - eto parametrizovannyj tip s parametrom T. Klass mozhno zadat' s pomoshch'yu konstrukcii imya-shablonnogo-klassa: imya-shablonnogo-klassa: imya-shablona-tipa < spisok-param-shablona-tipa > spisok-param-shablona-tipa: param-shablona-tipa spisok-param-shablona-tipa , param-shablona-tipa param-shablona: vyrazhenie imya-tipa Konstrukciya imya-shablonnogo-klassa yavlyaetsya imenem-klassa ($$R.9). Klass, kotoryj porozhdaetsya shablonom tipa dlya klassa, nazyvaetsya shablonnym klassom i ekvivalenten obychnomu klassu, opredelennomu so special'nym imenem - imenem-shablonnogo-klassa, sm. $$R.14.5. Esli v konstrukcii imya-shablonnogo-klassa imya-shablona-tipa ne opredeleno, to ona oboznachaet neopredelennyj klass. Imya shablonnogo klassa dolzhno byt' unikal'nym v programme i v svoej oblasti vidimosti ono ne mozhet oboznachat' drugoj shablon tipa, klass, funkciyu, ob容kt, znachenie ili tip. Tipy, ukazannye v spiske-param-shablona-tipa iz imeni-shablonnogo-klassa, dolzhny sootvetstvovat' tipam, zadannym v spiske-parametrov-shablona-tipa iz shablona-tipa. (Mozhno skazat', chto pervye yavlyayutsya fakticheskimi parametrami shablona tipa, a vtorye - formal'nymi.) Krome tipov v spiske-param-shablona-tipa mogut byt': vyrazheniya-konstanty, adresa ob容ktov ili funkcij, podlezhashchih vneshnemu svyazyvaniyu, staticheskie chleny klassov. Dlya parametrov, ne yavlyayushchihsya tipami, trebuetsya tochnoe sootvetstvie ($$R.13.2). Privedem primery ispol'zovaniya klassov shablona tipa vector: vector<int> v1(20); vector<complex> v2(30); typedef vector<complex> cvec; // cvec stanovitsya sinonimom // vector<complex> cvec v3(40); // v2 i v3 odnogo tipa v1[3] = 7; v2[3] = v3.elem(4) = complex(7,8); Zdes' vector<int> i vector<complex> yavlyayutsya shablonnymi klassami, i ih opredeleniya berutsya po umolchaniyu iz shablona tipa vector. Poskol'ku shablonnoe-imya-klassa yavlyaetsya imenem-klassa, to ono mozhet ispol'zovat'sya tam, gde dopustimo imya-klassa, naprimer: class vector<Shape*> vector<Window>* current_window; class svector : public vector<Shape*> { /* ... */ }; Opredelenie funkcii-chlena shablonnogo klassa dano v $$R.14.6. R.14.3 |kvivalentnost' tipov Dve konstrukcii shablonnoe-imya-klassa oboznachayut odin i tot zhe klass, esli v nih sovpadayut imena shablonov tipa i znacheniya ukazannyh parametrov. Naprimer, v sleduyushchih opisaniyah x i y odnogo tipa, kotoryj otlichen ot tipa z: template<class E, int size> class buffer; buffer<char, 2*512> x; buffer<char,1024> y; buffer<char,512> z; Nizhe privedeny opisaniya, v kotoryh odinakovyj tip imeyut x2 i x3. On otlichaetsya ot tipov x1 i x4: template<class T, void(*err_fct)()> class list { /* ... */ }; list<int,&error_handler1> x1; list<int,&error_handler2> x2; list<int,&error_handler2> x3; list<char,&error_handler2> x4; R.14.4 SHablony tipa dlya funkcij SHablon tipa dlya funkcii opredelyaet kak budet stroit'sya funkciya. Naprimer, semejstvo funkcij sort mozhno opisat' sleduyushchim obrazom: template<class T> void sort(vector<T>); SHablon tipa dlya funkcii porozhdaet neogranichennoe mnozhestvo peregruzhennyh funkcij. Funkciya, porozhdaemaya shablonom tipa dlya funkcij, nazyvaetsya shablonnoj funkciej. Ona ekvivalentna funkcii, v opisanii kotoroj ukazan tip, sootvetstvuyushchij shablonu, sm. $$R.14.5. Pri vyzove shablonnoj funkcii parametry shablona tipa ne zadayutsya yavno, vmesto etogo primenyaetsya pravilo razresheniya neopredelennosti peregruzhennyh funkcij. Rassmotrim primer: vector<complex> cv(100); vector<int> ci(200); void f(vector<complex>& cv, vector<int>& ci) { sort(cv); // vyzyvaetsya sort(vector<complex>) sort(ci); // vyzyvaetsya sort(vector<int>) } SHablonnaya funkciya mozhet byt' peregruzhena kak obychnymi, tak i shablonnymi funkciyami s tem zhe imenem. Dlya razresheniya neopredelennosti shablonnyh i obychnyh funkcij s odnim i tem zhe imenem nado posledovatel'no projti tri shaga: [1] Popytat'sya najti tochno sopostavimuyu vyzovu ($$R.13.2) funkciyu, i esli ona najdena, vyzvat' ee. [2] Popytat'sya najti shablon tipa dlya funkcij, po kotoromu mozhno sozdat' tochno sopostavimuyu s rassmatrivaemym vyzovom funkciyu. Esli udalos' najti, to vyzvat' funkciyu. [3] Popytat'sya primenit' obychnoe pravilo razresheniya neopredelennosti peregruzhennyh funkcij ($$R.13.2). Esli s ego pomoshch'yu funkciya najdena, vyzvat' ee. Esli ne najdeno sopostavimoj funkcii, vyzov yavlyaetsya oshibochnym. Esli uzhe na pervom shage najdeno bolee odnogo kandidata, sopostavimogo s dannoj funkciej, to vyzov takzhe schitaetsya neodnoznachnym i oshibochnym. Uspeshnoe vypolnenie shaga [2] privedet k sozdaniyu nekotoroj shablonnoj funkcii s parametrami ($$R.14.5), tipy kotoryh tochno sopostavyatsya s tipami parametrov, ukazannyh v vyzove. V etom sluchae nedopustimo rashozhdenie dazhe za schet trivial'nyh preobrazovanij ($$R.13.2). Takie zhe dejstviya primenyayutsya dlya sopostavleniya tipov ukazatelej na funkcii ($$R.13.3). Rassmotrim primer: template<class T> T max(T a, T b) { return a>b?a:b; }; void f(int a, int b, char c, char d) { int m1 = max(a,b); // max(int a, int b) char m2 = max(c,d); // max(char c, char b) int m3 = max(a,c); // oshibka: nel'zya sozdat' max(int,char) } Dobaviv k etomu primeru opisanie int max(int,int); mozhno razreshit' neopredelennost' dlya tret'ego vyzova, poskol'ku teper' zadana funkciya, kotoraya posle standartnogo preobrazovaniya char v int, mozhet sopostavit'sya s vyzovom max(a,c). Opredelenie shablona tipa dlya funkcii ispol'zuetsya dlya sozdaniya razlichnyh variantov shablona tipa. Dlya vyzova opredelennogo varianta dostatochno lish' opisaniya shablona tipa. Kazhdyj parametr-shablona-tipa, kotoryj priveden v spiske-parametrov-shablona-tipa dolzhen obyazatel'no ispol'zovat'sya pri zadanii tipov parametrov v shablone tipa dlya funkcii. template<class T> T* create(); //oshibka template<class T> void f() { // oshibka T a; // ... } Vse parametry-shablona-tipa, privedennye v shablone tipa dlya funkcii, dolzhny byt' parametrami-tipa. R.14.5 Opisaniya i opredeleniya Dlya kazhdogo imeni shablona tipa v programme dolzhno sushchestvovat' tol'ko odno opredelenie. Opisanij mozhet byt' neskol'ko. Opredelenie ispol'zuetsya dlya sozdaniya special'nyh shablonnyh klassov i shablonnyh funkcij, kotorye budut sootvetstvovat' shablonu tipa. Konstrukciya imya-shablonnogo-klassa vvodit opisanie shablonnogo klassa. Vyzov shablonnoj funkcii ili vzyatie ee adresa vvodit opisanie shablonnoj funkcii. Dlya vyzova ili vzyatiya adresa shablonnoj funkcii v yazyke sushchestvuet osoboe soglashenie: imya shablonnoj funkcii ispol'zuetsya tochno tak zhe kak imya obychnoj funkcii. Opisanie funkcii s takim zhe imenem, kak u shablonnoj funkcii, i s sopostavimymi tipami parametrov, vvodit opisanie special'noj shablonnoj funkcii. Esli dlya vypolneniya nekotoryh operacij trebuetsya opredelenie special'nogo shablonnogo klassa ili special'noj shablonnoj funkcii, i esli takogo opredeleniya v programme net, to ono budet sozdano. Opredelenie obychnoj (neshablonnoj) funkcii s tipom, kotoryj tochno sopostavlyaetsya s tipom iz opisaniya shablonnoj funkcii, schitaetsya opredeleniem special'noj shablonnoj funkcii. Rassmotrim primer: template<class T> void sort(vector<T>& v) { /* ... */ } void sort(vector<char*>& v) { /* ... */ } Zdes' opredelenie funkcii sort budet ispol'zovat'sya dlya toj funkcii iz semejstva sort, kotoraya sopostavlyaetsya pri vyzove s tipom parametra vector<char*>. Dlya drugih tipov vector budet sozdavat'sya sootvetstvuyushchee im opredelenie funkcii po shablonu tipa. Mozhno opredelit' klass, kotoryj zadaet shablonnyj klass, naprimer: template<class T> class stream { /* ... */ }; class stream<char> { /* ... */ }; Zdes' opisanie klassa budet ispol'zovat'sya v kachestve opredeleniya potoka simvolov (stream<char>). Drugie potoki budut upravlyat'sya s pomoshch'yu shablonnyh funkcij, sozdavaemyh po shablonu tipa dlya funkcij. Poka ne poyavitsya opisanie shablona tipa dlya klassa, nikakie operacii, kotorym trebuetsya opredelennyj klass, ne mogut byt' proizvedeny nad shablonnym klassom. Posle etogo special'nyj shablonnyj klass budet schitat'sya opredelennym, prichem srazu zhe pered pervym global'nym opisaniem, ispol'zuyushchim ego imya. R.14.6 Funkcii-chleny shablonov tipa Funkciya-chlen shablonnogo klassa schitaetsya neyavnoj shablonnoj funkciej, a parametry shablona tipa dlya ee klassa - ee shablonnymi parametrami. Privedem primer, v kotorom opisany tri shablona tipa dlya funkcii: template<class T> class vector { T* v; int sz; public: vector(int); T& operator[](int); T& elem(int i) { return v[i]; } // ... }; Funkciyu, vypolnyayushchuyu indeksaciyu, mozhno opredelit' sleduyushchim obrazom: template<class T> T& vector<T>::operator[](int i) { if (i<0 || sz>=i) error("vector: range error"); return v[i]; } SHablonnyj parametr dlya vector<T>::operator[]() budet zadavat'sya tem tipom vector, k kotoromu primenyaetsya operaciya indeksacii. vector<int> v1(20); vector<complex> v2(30); v1[3] = 7; // vector<int>::operator[]() v2[3] = complex(7,8); // vector<complex>::operator[]() R.14.7 Druz'ya Funkciya-drug dlya shablona tipa ne yavlyaetsya neyavnoj shablonnoj funkciej, naprimer: template<class T> class task { // ... friend void next_time(); friend task<T>* preempt(task<T>*); friend task* prmt(task*); // oshibka // ... }; Zdes' funkciya next_time() stanovitsya drugom vseh klassov task, a kazhdyj klass task imeet v kachestve druga funkciyu preempt() c sootvetstvuyushchimi tipami parametrov. Funkciyu preempt() mozhno opredelit' kak shablon tipa. template<class T> task<T>* preempt(task<T>* t) { /* ... */ } Opisanie funkcii prmt() yavlyaetsya oshibochnym, poskol'ku tipa task ne sushchestvuet, a est' tol'ko special'nye shablonnye tipy task<int>, task<record>, i t.d. R.14.8 Staticheskie chleny i peremennye Dlya kazhdogo shablonnogo klassa ili funkcii, sozdavaemyh po shablonu tipa, obrazuetsya svoya kopiya staticheskih peremennyh ili chlenov. Rassmotrim primer: template<class T> class X { static T s; // ... }; X<int> aa; X<char*> bb; Zdes' v klasse X<int> est' staticheskij chlen tipa int, a v klasse X<char> est' staticheskij chlen tipa char*. Analogichno, v privedennom nizhe primere, funkciya f(int*) imeet staticheskij chlen s tipa int, a funkciya f(char**) imeet staticheskij chlen tipa char**: template<class T> f(T* p) { static T s; // ... } void g(int a, char* b) { f(&a); f(&b); } R.15 Obrabotka osobyh situacij R.15.1 Obrabotka osobyh situacij Pri obrabotke osobyh situacij v hode vypolneniya programmy informaciya i upravlenie peredayutsya iz nekotoroj tochki obrabotchiku osobyh situacij. Obrabotchik nahoditsya v cepochke vypolnennyh vyzovov funkcij. Upravlenie obrabotchiku peredaetsya s pomoshch'yu vyrazheniya-zapuska, kotoroe mozhet byt' tol'ko v proveryaemom-bloke obrabotchika ili v funkcii, vyzvannoj iz proveryaemogo-bloka. proveryaemyj-blok: try sostavnoj-operator spisok-obrabotchikov spisok-obrabotchikov: obrabotchik spisok-obrabotchikov opt obrabotchik: catch ( opisanie-osoboj-situacii ) sostavnoj-operator opisanie-osoboj-situacii: spisok-specifikacij-tipa opisatel' spisok-specifikacij-tipa abstraktnyj-opisatel' spisok-specifikacij-tipa ... vyrazhenie-zapuska: throw vyrazhenie opt Konstrukciya proveryaemyj-blok yavlyaetsya operatorom ($$R.6), a vyrazhenie-zapuska - unarnym vyrazheniem tipa void ($$R.5). Inogda vyrazhenie-zapuska nazyvayut "tochkoj zapuska", a pro funkciyu, v kotoroj vstretilos' vyrazhenie-zapuska, govoryat, chto ona "zapuskaet osobuyu situaciyu. CHast' programmy, kotoroj peredaetsya upravlenie iz tochki zapuska nazyvaetsya obrabotchikom. R.15.2 Zapusk osoboj situacii Pri zapuske osoboj situacii upravlenie peredaetsya obrabotchiku. Zapusk soprovozhdaetsya peredachej ob容kt, tip kotorogo opredelyaet, kakoj obrabotchik dolzhen perehvatit' osobuyu situaciyu. Tak, vyrazhenie throw "Help!"; mozhet byt' perehvacheno nekotorym obrabotchikom s tipom char*: try { // ... } catch(const char* p) { // zdes' obrabatyvaetsya osobaya situaciya v simvol'nyh strokah } a osobaya situaciya Overflow (perepolnenie): class Overflow { // ... public: Overflow(char,double,double); }; void f(double x) { // ... throw Overflow('+',x,3.45e107); } mozhet byt' perehvachena obrabotchikom try { // ... f(1.2); // ... } catch(Overflow& oo) { // zdes' obrabotka osoboj situacii tipa Overflow } Pri zapuske osoboj situacii upravlenie peredaetsya blizhajshemu obrabotchiku sootvetstvuyushchego tipa. "Blizhajshij" - eto obrabotchik, proveryaemyj-blok kotorogo poslednim poluchil upravlenie i ono eshche ne bylo peredano ottuda. CHto takoe "sootvetstvuyushchij" tip opredelyaetsya v $$R.15.4. Pri vypolnenii vyrazheniya-zapuska sozdaetsya vremennyj ob容kt staticheskogo tipa, kotoryj sluzhit operandom v komande throw, |tot ob容kt ispol'zuetsya dlya inicializacii peremennoj, sootvetstvuyushchego tipa, opisannoj v obrabotchike. Esli ne schitat' ogranichenij na sopostavlenie tipov (sm. $$R.15.4) i ispol'zovanie vremennoj peremennoj, to operand throw analogichen parametru funkcii pri vyzove ($$R.5.2.2) ili operandu v operatore return. Esli mozhno bylo by, ne menyaya smysla programmy za schet otkaza ot vyzovov konstruktorov i destruktorov dlya vremennogo ob容kta ($$R.12.1), obojtis' sovsem bez vremennogo ob容kta, to osobuyu situaciyu mozhno bylo by neposredstvenno inicializirovat' v obrabotchike parametrom vyrazheniya zapuska. Esli v vyrazhenii-zapuska operand ne zadan, to proishodit perezapusk obrabotki osoboj situacii. Takoe vyrazhenie-zapuska mozhet poyavitsya tol'ko v samom obrabotchike ili v funkcii, kotoraya neposredstvenno ili oposredovanno vyzyvaetsya iz nego. Naprimer, fragment programmy, kotoryj vypolnyaetsya pri obrabotke osoboj situacii, esli nel'zya eshche polnost'yu provesti etu obrabotku, mozhet vyglyadet' tak: try { // ... } catch (...) { // perehvat vseh osobyh situacij // (chastichnaya) obrabotka osobyh situacij throw; // peredacha ostal'nyh osobyh situacij drugomu obrabotchiku } R.15.3 Konstruktory i destruktory Kogda upravlenie peredaetsya iz tochki zapuska osoboj situacii obrabotchiku, to vyzyvayutsya destruktory dlya vseh avtomaticheskih ob容ktov, postroennyh s momenta vhoda v proveryaemyj-blok. Esli ob容kt ne byl postroen polnost'yu, to destruktory vyzyvayutsya tol'ko dlya polnost'yu postroennyh vlozhennyh v nego ob容ktov. Krome togo, esli osobaya situaciya zapuskaetsya v konstruktore pri postroenii elementa avtomaticheskogo massiva, to unichtozhat'sya budut tol'ko uzhe postroennye elementy etogo massiva. Process vyzova destruktorov dlya unichtozheniya avtomaticheskih ob容ktov, postroennyh v hode vypolneniya programmy ot nachala proveryaemogo-bloka do vyrazheniya-zapuska, nazyvaetsya "raskruchivaniem steka". R.15.4 Obrabotka osoboj situacii Obrabotchik tipa T, const T, T& ili const& sopostavim s vyrazheniem-zapuska, imeyushchim operand tipa E, esli: [1] T i E yavlyayutsya odnim tipom; [2] T yavlyaetsya dostupnym ($$R.4.6) bazovym klassom E v tochke zapuska; [3] T yavlyaetsya tipom ukazatelya, a E yavlyaetsya takim tipom ukazatelya, kotoryj mozhno v tochke zapuska preobrazovat' v T s pomoshch'yu standartnyh preobrazovanij ukazatelya ($$R.4.6). Rassmotrim primer: class Matherr { /* ... */ virtual vf(); }; class Overflow : public Matherr { /* ... */ }; class Underflow : public Matherr { /* ... */ }; class Zerodivide : public Matherr { /* ... */ }; void f() { try { g(); } catch (Overflow oo) { // ... } catch (Matherr mm) { // ... } } Zdes' obrabotchik Overflow budet perehvatyvat' situacii tipa Overflow, a obrabotchik Matherr budet perehvatyvat' situacii tipa Matherr i vseh tipov, yavlyayushchihsya obshchimi proizvodnymi ot Matherr, vklyuchaya Underflow i Zerodivide. Obrabotchiki v proveryaemom-bloke podbirayutsya dlya dannoj osoboj situacii v poryadke ih opisaniya. Schitaetsya oshibkoj , esli obrabotchik dlya bazovogo klassa pomeshchen pered obrabotchikom dlya proizvodnogo klassa, poskol'ku pri takom raspolozhenii upravlenie nikogda ne popadet k obrabotchiku dlya proizvodnogo klassa. |llipsis ... v opisanii-osoboj-situacii dejstvuet tak zhe kak, i v opisanii parametrov funkcii, on sopostavim s lyuboj osoboj situaciej. Esli zadan ellipsis, to ispol'zuyushchij ego obrabotchik dolzhen idti poslednim v proveryaemom-bloke. Esli v proveryaemom-bloke ne proizoshlo sopostavleniya ni s odnim iz obrabotchikov, poisk sootvetstvuyushchego obrabotchika prodolzhaetsya v dinamicheski ob容mlyushchem proveryaemom-bloke. Esli vo vsej programme ne proizoshlo sopostavleniya ni s odnim obrabotchikom, vyzyvaetsya funkciya terminate() ($$R.15.7). Osobaya situaciya schitaetsya obrabotannoj posle vhoda v telo obrabotchika. V etot moment zavershitsya "raskruchivanie steka". R.15.5 Specifikacii osobyh situacij Vozniknovenie i perehvatyvanie osoboj situacii vliyaet na vzaimodejstvie funkcij. Spisok osobyh situacij, kotorye pryamo ili kosvenno mozhet zapustit' dannaya funkciya, mozhno zadat' kak chast' ee opisaniya. Konstrukciya specifikaciya-osoboj-situacii predshestvuet opisatelyu funkcii. specifikaciya-osoboj-situacii: throw ( spisok-tipov opt ) spisok-tipov: imya-tipa spisok-tipov , imya-tipa Privedem primer: void f() throw (X,Y) { // ... } Esli funkciya poprobuet zapustit' neukazannuyu v spiske situaciyu, upravlenie peredaetsya funkcii unexpected(), sm. $$R.15.8. Realizaciya yazyka ne dolzhna zapreshchat' vyrazhenie tol'ko potomu, chto pri ego vychislenii vozmozhen zapusk osoboj situacii, ne ukazannoj v specifikacii-osoboj situacii opisaniya funkcii. Obrabotka nepredvidennyh osobyh situacij proishodit v dinamike. Funkciya, v kotoroj otsutstvuet specifikaciya-osoboj-situacii, mozhet zapustit' lyubuyu osobuyu situaciyu. Funkciya s pustoj specifikaciej-osobyh-situacij (throw()) ne dolzhna zapuskat' nikakih osobyh situacij. Esli funkciya mozhet zapustit' osobuyu situaciyu klassa X, to ona mozhet zapustit' osobuyu situaciyu lyubogo klassa, yavlyayushchegosya obshchim proizvodnym klassom ot X. Konstrukciya specifikaciya-osoboj-situacii ne otnositsya k tipu funkcii. R.15.6 Special'nye funkcii Mehanizm upravleniya osobymi situaciyami ispol'zuet dlya reakcii na oshibki pri samoj obrabotke osobyh situacij funkcii: terminate() i unexpected(). R.15.6.1 Funkciya terminate() Inogda ot predusmotrennoj obrabotki osobyh situacij prihoditsya perehodit' k bolee grubym priemam, naprimer: - kogda mehanizm upravleniya osobymi situaciyami ne smog najti obrabotchik dlya zapushchennoj osoboj situacii; - kogda mehanizm upravleniya osobymi situaciyami stolknulsya s narushennoj strukturoj steka; - kogda destruktor, vyzvannyj v processe raskrutki steka pri zapuske osoboj situacii, sam pytaetsya zavershit' vypolnenie programmy, zapustiv osobuyu situaciyu. V etih sluchayah vyzyvaetsya funkciya void terminate(); Ona v svoyu ochered' vyzyvaet funkciyu, kotoraya byla ukazana kak parametr pri poslednem obrashchenii k set_terminate(): typedef void(*PFV)(); PFV set_terminate(PFV); Funkciya, kotoraya byla zadana v predydushchem vyzove set_terminate(), budet vozvrashchaemym znacheniem tekushchego vyzova. |to pomogaet pol'zovatelyu realizovat' algoritm vosstanovleniya steka. Po umolchaniyu funkciya terminate() vyzyvaet abort(). Vybor s pomoshch'yu terminate() takoj funkcii, kotoraya vmesto dejstvitel'nogo zaversheniya programmy, pytaetsya vernut'sya v vyzvavshuyu programmu, yavlyaetsya oshibkoj. R.15.6.2 Funkciya unexpected() Esli funkciya, imeyushchaya specifikaciyu-osoboj-situacii, zapuskaet neukazannuyu osobuyu situaciyu, to vyzyvaetsya funkciya void unexpected(); Ona v svoyu ochered' vyzyvaet funkciyu, kotoraya byla zadana kak parametr pri poslednem obrashchenii k set_unexpected(): typedef void(*PFV)(); PFV set_unexpected(PFV); Funkciya, kotoraya byla zadana v predydushchem vyzove set_unexpected(), budet vozvrashchaemym znacheniem tekushchego vyzova. |to pomogaet pol'zovatelyu realizovat' algoritm vosstanovleniya steka. Po umolchaniyu unexpected() vyzyvaet terminate(). Poskol'ku po umolchaniyu terminate() vyzyvaet abort(), rezul'tatom budet neposredstvennoe i tochnoe obnaruzhenie oshibki. R.15.7 Osobye situacii i pravila dostupa Dlya formal'nogo parametra operacii catch dejstvuyut takie zhe pravila dostupa, kak i dlya formal'nogo parametra funkcii, v kotoroj zadana operaciya catch. Pri zapuske osoboj situacii mozhno ukazyvat' takoj ob容kt, kotoryj mozhno kopirovat' i unichtozhat' v oblasti vidimosti funkcii, gde zadana operaciya throw. R.16 Preprocessornaya obrabotka Realizaciya yazyka S++ vklyuchaet preprocessor s vozmozhnostyami makropodstanovki, uslovnoj translyacii i vklyucheniya ukazannyh fajlov. Dlya peredachi zadanij preprocessoru sluzhat stroki, nachinayushchiesya s simvola # (pered nim mogut idti probely i simvoly gorizontal'noj tabulyacii). Takie stroki nazyvayutsya komandami, i ih sintaksis opredelyaetsya nezavisimo ot ostal'nogo yazyka. Komandy mogut nahodit'sya v lyubom meste programmy, i ih dejstvie prodolzhaetsya (nezavisimo ot pravil oblastej vidimosti S++) do konca dannoj edinicy translyacii ($$R.2). Komandu preprocessora, kak i lyubuyu stroku, mozhno prodolzhit' na sleduyushchej stroke vhodnogo teksta, pomestiv simvol obratnoj drobnoj cherty neposredstvenno pered simvolom konca prodolzhaemoj stroki. Preprocessor do togo, kak vhodnaya stroka budet razbita na leksemy, udalyaet simvoly obratnoj drobnoj cherty i konca stroki. Simvol obratnoj drobnoj cherty ne dolzhen byt' poslednim simvolom vhodnogo fajla. K leksemam preprocessora otnosyatsya: leksemy samogo yazyka ($$R.2.1), imya fajla, kotoroe ispol'zuetsya v komande #include i voobshche lyuboj simvol, otlichnyj ot obobshchennogo probela i nesovpadayushchij ni s kakoj iz leksem preprocessora. R.16.1 Fazy preprocessornoj obrabotki Po opredeleniyu sushchestvuet neskol'ko faz preprocessornoj obrabotki. V konkretnoj realizacii fazy mogut slivat'sya, no rezul'tat vse ravno dolzhen byt' takim, kak budto byli vypolneny vse fazy. Perechislim ih. Pri neobhodimosti simvoly, zavisyashchie ot sistemy simvoly, oboznachayushchie konec stroki, zamenyayutsya na standartnyj simvol konca stroki. Analogichnoj zamene podlezhat vse zavisyashchie ot sistemy simvoly. Opredelennye posledovatel'nosti simvolov (trigrafy) zamenyayutsya na ekvivalentnyj im otdel'nyj simvol ($$R.16.2). Udalyayutsya vse takie pary simvolov: obratnaya drobnaya cherta, sleduyushchij za nej simvol konca stroki. V rezul'tate budut slity stroki vhodnogo teksta, iz kotoryh byla udalena eta para. Vhodnoj tekst razbivaetsya na leksemy preprocessora i posledovatel'nost' obobshchennyh probelov. Kazhdyj kommentarij zamenyaetsya na odin probel. Vhodnoj tekst ne dolzhen konchat'sya posredi leksemy ili kommentariya. Vypolnyayutsya komandy preprocessora, i proizvodyatsya makropodstanovki ($$R.16.3, $$R.16.4, $$R.16.5, $$R.16.6, $$R.16.7 i $$R.16.8). V simvol'nyh konstantah i strokah literalov kombinacii special'nyh simvolov zamenyayutsya na svoi ekvivalenty ($$R.2.5.2). Slivayutsya sosednie stroki literalov. Rezul'tat preprocessornoj obrabotki podvergaetsya sintaksicheskomu i semanticheskomu analizu, transliruetsya, a zatem svyazyvaetsya s neobhodimymi bibliotekami i drugimi programmami. R.16.2 Trigrafnye posledovatel'nosti Prezhde chem nachnetsya kakaya-libo inaya preprocessornaya obrabotka, kazhdoe vhozhdenie trigrafnoj posledovatel'nosti zamenyaetsya na odin simvol v sootvetstvii s privedennoj nizhe tablicej. ??= # ??( [ ??/ \ ??) [ ??' ^ ??! | Naprimer, stroka ??=define arraycheck(a,b) a??(b??) ??!??! b??(a??) preobrazuetsya v #define arraycheck(a,b) a[b] || b[a] R.16.3 Makroopredelenie i makropodstanovka Komanda vida #define identifikator stroka-leksem nazyvaetsya makroopredeleniem. Ona ukazyvaet preprocessoru, chto nado proizvesti zamenu vseh posleduyushchih vhozhdenij identifikatora na zadannuyu posledovatel'nost' leksem, nazyvaemuyu strokoj zameny. Obobshchennye probely, okruzhayushchie etu posledovatel'nost' leksem, otbrasyvayutsya. Naprimer, pri opredelenii #define SIDE 8 opisanie char chessboard[side][side]; posle makropodstanovki primet vid char chessboard[8][8]; Opredelennyj takim sposobom identifikator mozhno pereopredelit' s pomoshch'yu drugoj komandy #define, no pri uslovii, chto stroki zameny v oboih opredeleniyah sovpadayut. Vse simvoly obobshchennogo probela, razdelyayushchie leksemy, schitayutsya identichnymi. Komanda vida identifikator ( identifikator, ... ,identifikator) stroka-leksem nazyvaetsya makroopredeleniem s parametrami ili "funkcional'nym" makroopredeleniem. V nem nedopustimy probely mezhdu pervym identifikatorom i simvolom (. Opredelennyj takim sposobom identifikator mozhno pereopredelit' s pomoshch'yu drugogo funkcional'nogo makroopredeleniya, no pri uslovii, chto vo vtorom opredelenii to zhe chislo i te zhe naimenovaniya parametrov, chto i v pervom, a obe stroki zameny sovpadayut. Vse simvoly obobshchennogo probela, razdelyayushchie leksemy, schitayutsya identichnymi. Posleduyushchie vhozhdeniya identifikatora, opredelennogo v funkcional'nom makroopredelenii, esli za nim sleduyut simvol (, posledovatel'nost' leksem, razdelennyh zapyatymi, i simvol ), zamenyayutsya na stroku leksem iz makroopredeleniya. Obobshchennye probely, okruzhayushchie stroku zameny, otbrasyvayutsya. Kazhdoe vhozhdenie identifikatora, iz spiska parametrov makroopredeleniya, zamenyaetsya na posledovatel'nost' leksem, predstavlyayushchuyu sootvetstvuyushchij fakticheskij parametr v makrovyzove. Fakticheskimi parametrami yavlyayutsya stroki leksem, razdelennye zapyatymi. Zapyataya, vzyataya v kavychki, ili nahodyashchayasya v simvol'noj konstante ili vo vlozhennyh kruglyh skobkah, ne razdelyaet parametrov. CHislo fakticheskih parametrov makrovyzova dolzhno sovpadat' s chislom parametrov makroopredeleniya. Posle identifikacii parametrov dlya funkcional'nogo makroopredeleniya proishodit podstanovka fakticheskih parametrov. Posle vypolneniya podstanovok v parametre (esli oni byli) etot parametr v stroke zameny zameshchaetsya fakticheskim parametrom iz makrovyzova ($$R.16.3.3); isklyucheniya sostavlyayut sluchai, kogda parametru predshestvuet leksema # ($$R.16.3.1), ili s nim sosedstvuet leksema ## ($$R.16.3.2). Privedem primer. Pust' est' makroopredeleniya #define index_mask 0XFF00 #define extract(word,mask) word & mask Togda makrovyzov index = extract(packed_data,index_mask); posle podstanovki primet vid index = packed_data & 0XFF00; Dlya oboih vidov makroopredelenij stroka zameny proveryaetsya na nalichie drugih makroopredelenij ($$R.16.3.3). R.16.3.1 Operaciya # Esli neposredstvenno pered parametrom v stroke zameny idet leksema #, to pri podstanovke parametr i operaciya # budut zameneny na stroku literalov , soderzhashchuyu imya sootvetstvuyushchego parametra makrovyzova. V simvol'noj konstante ili stroke literalov, vhodyashchih v parametr, pered kazhdym vhozhdeniem \ ili " vstavlyaetsya simvol \. Naprimer, esli est' makroopredeleniya #define path(logid,cmd) "/usr/" #logid "/bin/" #cmd to makrovyzov char* mytool=path(joe,readmail); privedet k takomu rezul'tatu: char* mytool="/usr/" "joe" "/bin/" "readmail"; Posle konkatenacii sosednih strok ($$R.16.1) poluchim: char* mytool="/usr/joe/bin/readmail"; R.16.3.2 Operaciya ## Esli v stroke zameny mezhdu dvumya leksemami, odna iz kotoryh predstavlyaet parametr makroopredeleniya, poyavlyaetsya operaciya ##, to sama operaciya ## i okruzhayushchie ee obobshchennye probely udalyayutsya. Takim obrazom, rezul'tat operacii ## sostoit v konkatenacii. Pust' est' makroopredelenie, #define inherit(basenum) public Pubbase ## basenum, \ private Privbase ## basenum togda makrovyzov class D : inherit(1) { }; privedet k takomu rezul'tatu: class D : public Pubbase1, Privbase1 { }; Makroopredelenie, kotoroe v stroke zameny sosedstvuet s ##, ne podlezhit podstanovke, odnako, rezul'tat konkatenacii mozhet ispol'zovat'sya dlya podstanovki. Privedem primer. Pust' est' opredeleniya: #define concat(a) a ## ball #define base B #define baseball sport Togda makrovyzov concat(base) dast v rezul'tate sport a vovse ne Bball R.16.3.3 Povtornyj prosmotr i dal'nejshie podstanovki Posle togo, kak v stroke zameny proizoshla podstanovka vseh parametrov makrovyzova, poluchivshayasya stroka prosmatrivaetsya povtorno dlya obnaruzheniya dopolnitel'nyh makroopredelenij. Esli v processe povtornyh prosmotrov stroki zameny najdeno imya makroopredeleniya, to podstanovka vse zhe ne proishodit. Rekursivnuyu podstanovku nel'zya vypolnit' kak komandu preprocessora, hotya ona kazhetsya dlya nego estestvennoj komandoj. R.16.3.4 Oblast' vidimosti makroimen i konstrukciya #undef Posle poyavleniya makroopredeleniya identifikator iz nego schitaetsya opredelennym i ostaetsya v tekushchej oblasti vidimosti (nezavisimo ot pravil oblastej vidimosti v S++) do konca edinicy translyacii ili poka ego opredelenie ne budet otmeneno s pomoshch'yu komandy #undef. Komanda #undef imeet vid: #undef identifikator Ona zastavlyaet preprocessor "zabyt'" makroopredelenie s etim identifikatorom. Esli ukazannyj identifikator ne yavlyaetsya opredelennym v dannyj moment makroimenem, to komanda #undef ignoriruetsya. R.16.4 Vklyuchenie fajlov Upravlyayushchaya stroka vida: #include <imyafajla> privodit k zamene dannoj stroki na soderzhimoe fajla s ukazannym imenem. Poisk ukazannogo fajla prohodit v opredelennoj posledovatel'nosti chastej arhiva sistemy i opredelyaetsya realizaciej. Analogichno, upravlyayushchaya stroka vida: #include "imyafajla" privodit k zamene dannoj stroki na soderzhimoe fajla s ukazannym imenem. Poisk etogo fajla nachinaetsya v osobyh (sistemnyh) chastyah arhiva, ukazannyh v nachale posledovatel'nosti poiska. Esli tam on ne najden, to poisk fajla idet po vsej posledovatel'nosti, kak esli by upravlyayushchaya stroka imela vid: #include <imyafajla> V imeni fajla, ogranichennom simvolami < i > nel'zya ispol'zovat' simvoly konca stroki ili >. Esli v takom imeni poyavitsya odin iz simvolov ', \, ili ", a takzhe posledovatel'nost' simvolov /* ili //, to rezul'tat schitaetsya neopredelennym. V imeni fajla, ogranichennom paroj simvolov " nel'zya ispol'zovat' simvoly konca stroki ili ", hotya simvol > dopustim. Esli v takom imeni poyavitsya simvol ' ili \ ili posledovatel'nost' /* ili //, to rezul'tat schitaetsya neopredelennym. Esli komanda #include stroka-leksem imeet vid, sootvetstvuyushchij ni pervoj, ni vtoroj upravlyayushchej stroke, to leksemy preprocessora, zadannye v etoj komande obrabatyvayutsya kak obychnyj tekst. V rezul'tate dolzhna poluchit'sya komanda, vid kotoroj sootvetstvuet odnomu iz privedennyh. Ona i budet vypolnena kak polozheno. Komanda #include mozhet byt' v fajle, kotoryj sam poyavilsya v rezul'tate vypolneniya drugoj komandy #include. Realizaciya mozhet nakladyvat' ogranichenie na glubinu vlozhennosti komandy #include vo vhodnyh fajlah programmy, kotorye prihoditsya chitat' dlya vypolneniya pervonachal'noj komandy #include v odnom iz vhodnyh fajlov. R.16.5 Uslovnaya translyaciya S pomoshch'yu preprocessora mozhno organizovat' uslovnuyu translyaciyu programmy. Sintaksicheski eto zadaetsya sleduyushchim obrazom: uslovnoe: chast'-if chasti-elif opt chast'-else opt stroka-endif chast'-if: stroka-if tekst stroka-if: # if vyrazhenie-konstanta # ifdef identifikator # ifndef identifikator chasti-elif: stroka-elif tekst chasti-elif stroka-elif tekst stroka-elif: # elif vyrazhenie-konstanta chast'-else: stroka-else tekst stroka-else: # else stroka-endif: # endif Konstantnye vyrazheniya v #if i #elif (esli eti chasti est') vychislyayutsya v poryadke ih zadaniya v tekste do teh por, poka odno iz nih ne okazhetsya otlichnym ot nulya. Operatory S++, sleduyushchie za strokoj, v kotoroj vyrazhenie okazalos' ravnym nulyu, ne transliruyutsya. Komandy preprocessora, idushchie za etoj strokoj ignoriruyutsya. Posle togo, kak najdena komanda s nenulevym znacheniem vyrazheniya, tekst vseh posleduyushchih chastej #elif i #else (t.e. operatory S++ i komandy preprocessora) ignoriruetsya. Tekst, otnosyashchijsya k pervoj komande s nenulevym znacheniem vyrazheniya podlezhit obychnoj preprocessornoj obrabotke i translyacii. Esli znacheniya vseh vyrazhenij, ukazannyh v #if i #elif, okazalis' ravnymi nulyu, togda obychnoj obrabotke podlezhit tekst, otnosyashchijsya k #else. V vyrazhenii-konstante, kotoroe vstretilos' v #if ili #elif mozhno ispol'zovat' unarnuyu operaciyu defined, prichem v dvuh variantah: defined identifikator ili defined (identifikator) Esli eta operaciya primenyaetsya k identifikatoru, kotoryj byl opredelen s pomoshch'yu komandy #define, i esli eto opredelenie ne bylo otmeneno komandoj #undef, to rezul'tat raven 1, inache rezul'tat raven 0. Sam identifikator defined nel'zya pereopredelit', nel'zya i otmenit' ego opredelenie. Posle primeneniya operacij defined proishodit raskrytie vseh vseh makroopredelenij, imeyushchihsya v konstantnom vyrazhenii sm. $$R.16.3. V rezul'tate dolzhno poluchit'sya celochislennoe vyrazhenie-konstanta, otlichayushcheesya ot opredeleniya v $$R.5.19 tem, chto tipy int i unsigned int rassmatrivayutsya kak long i unsigned long sootvetstvenno, a krome togo v etom vyrazhenii ne dolzhno byt' operacij privedeniya, sizeof ili elementa perechisleniya. Upravlyayushchaya stroka #ifdef identifikator ekvivalentna stroke #if defined identifikator a upravlyayushchaya stroka #ifndef identifikator ekvivalentna stroke #if !defined identifikator Konstrukcii, zadayushchie uslovnuyu translyaciyu, mogut byt' vlozhennymi, no realizaciya mozhet nakladyvat' ogranichenie na glubinu vlozhennosti etih konstrukcij. R.16.6 Upravlenie strokami Dlya udobstva napisaniya programm, porozhdayushchih tekst na S++, vvedena upravlyayushchaya stroka vida: #line konstanta "imyafajla" opt Ona zadaet znachenie predopredelennomu makroimeni __LINE__ ($$R.16.10), kotoroe ispol'zuetsya v diagnosticheskih soobshcheniyah ili pri simvolicheskoj otladke; a imenno: nomer sleduyushchej stroki vhodnogo teksta schitaetsya ravnym zadannoj konstante, kotoraya dolzhna byt' desyatichnym celym chislom. Esli zadano "imyafajla", to znachenie makroimeni __FILE__ ($$R.16.10) stanovitsya ravnym imeni ukazannogo fajla. Esli ono ne zadano, __FILE__ ne menyaet svoego znacheniya. Makroopredeleniya v etoj upravlyayushchej stroke raskryvayutsya do vypolneniya samoj komandy. R.16.7 Komanda error Stroka vida: #error stroka-leksem zastavlyaet realizaciyu vydat' diagnosticheskoe soobshchenie, sostoyashchee iz zadannoj posledovatel'nosti leksem preprocessora. R.16.8 Komanda pragma Stroka vida: #pragma stroka-leksem zastavlyaet realizaciyu vesti sebya nekotorym opredelennym obrazom pri uslovii chto realizaciya "ponimaet" etu stroku. Lyubaya neraspoznannaya stroka #pragma ignoriruetsya. R.16.9 Pustaya direktiva Komanda preprocessora vida # ne okazyvaet nikakogo dejstviya. R.16.10 Predopredelennye makroimena V processe translyacii opredelennuyu informaciyu soderzhat sleduyushchie predopredelennye makroimena. __LINE__ desyatichnaya konstanta, soderzhashchaya nomer tekushchej stroki teksta programmy na S++. __FILE__ stroka literalov, predstavlyayushchaya imya transliruemogo vhodnogo fajla. __DAT