chlenam vlozhennogo klassa i podchinyayutsya obychnym pravilam dostupa, naprimer: class E { int x; class I { int y; void f(E* p, int i) { p->x = i; // oshibka: E::x chastnyj chlen } }; int g(I* p) { return p->y; // oshibka: I::y chastnyj chlen } }; Funkcii-chleny i predstavlyayushchie dannye, staticheskie chleny iz vlozhennogo klassa mozhno opredelit' v global'noj oblasti vidimosti, naprimer: class enclose { class inner { static int x; void f(int i); }; }; typedef enclose::inner ei; int ei::x = 1; void enclose::inner::f(int i) { /* ... */ } Podobno funkcii-chlenu druzhestvennaya funkciya, opredelennaya v dannom klasse, nahoditsya v oblasti vidimosti etogo klassa. Ona podchinyaetsya tem zhe pravilam svyazyvaniya imen, chto i funkcii-chleny (oni ukazany vyshe i v $$R.10.4), i ne imeet tak zhe kak oni osobyh prav dostupa k chlenam ob容mlyushchego klassa i k lokal'nym peremennym funkcij etogo klassa ($$R.11). R.9.8 Opisaniya lokal'nyh klassov Klass mozhno opisat' v opredelenii funkcii, takoj klass nazyvaetsya lokal'nym. Imya lokal'nogo klassa schitaetsya lokal'nym v ob容mlyushchej oblasti vidimosti, a oblast'yu vidimosti lokal'nogo klassa yavlyaetsya ob容mlyushchaya oblast' vidimosti. V opisaniyah lokal'nogo klassa iz ob容mlyushchej oblasti vidimosti mozhno ispol'zovat' tol'ko imena tipov, staticheskih peremennyh, vneshnih peremennyh i funkcij, a takzhe elementy perechisleniya. Privedem primer: int x; void f() { static int s; int x; extern int g(); struct local { int h() { return x; } // oshibka: `x' avtomaticheskaya int j() { return s; } // normal'no int k() { return ::x; } // normal'no int l() { return g(); } // normal'no } } Ob容mlyushchaya funkciya ne imeet osobyh prav dostupa k chlenam lokal'nogo klassa, ona podchinyaetsya obychnym pravilam dostupa ($$R.11). Funkciyu-chlen lokal'nogo klassa sleduet opredelyat' v opredelenii etogo klassa. Lokal'nyj klass ne mozhet imet' staticheskih chlenov, predstavlyayushchih dannye. R.9.9 Imena lokal'nyh tipov Imena tipov podchinyayutsya tochno takim zhe pravilam oblastej vidimosti, kak i drugie imena. V chastnosti, imena tipov, opredelennye v opisanii klassa, nel'zya ispol'zovat' vne etogo klassa bez utochneniya, naprimer: class X { public: typedef int I; class Y { /* ... */ } I a; }; I b; // oshibka Y c; // oshibka X::Y d; // oshibka Sleduyushchee polozhenie ogranichivaet zavisimost' ot konteksta pravil opisaniya chlenov klassa, a tak zhe pravila perenosa tela funkcij, yavlyayushchihsya podstanovkami. Posle ispol'zovaniya v opisanii klassa imya konstanty, imya-klassa ili imya-typedef ne mozhet pereopredelyat'sya v opisanii etogo klassa. Imya, ne yavlyayushcheesya imenem-klassa ili imenem-typedef ne mozhet byt' opredeleno v opisanii klassa kak imya-klassa ili imya-typedef, esli ono uzhe ispol'zovalos' inache v opisanii etogo klassa. Rassmotrim primer: typedef int c; enum { i = 1 }; class X { char v[i]; int f() { return sizeof(c); } char c; // oshibka: imya typedef // pereopredelyaetsya posle ispol'zovaniya enum { i = 2 }; // oshibka: `i' pereopredelyaetsya posle // ispol'zovaniya v zadanii tipa `char[i]' }; typedef char* T; struct Y { T a; typedef long T; // oshibka: imya T uzhe ispol'zovano T b; }; R.10 Proizvodnye klassy V opisanii klassa mozhno ukazat' spisok bazovyh klassov s pomoshch'yu sleduyushchih konstrukcij: spec-bazovyh: : spisok-bazovyh spisok-bazovyh: specifikaciya-bazovyh spisok-bazovyh , specifikaciya-bazovyh specifikaciya-bazovyh: polnoe-imya-klassa virtual specifikaciya-dostupa opt polnoe-imya-klassa specifikaciya-dostupa virtual opt polnoe-imya-klassa specifikaciya-dostupa: private protected public Konstrukciya imya-klassa v specifikacii-bazovyh dolzhna oboznachat' ranee opisannyj klass ($$R.9), kotoryj nazyvaetsya bazovym po otnosheniyu k opredelyaemomu klassu. Govoryat, chto klass yavlyaetsya proizvodnym ot svoih bazovyh klassov. Naznachenie konstrukcii specifikaciya-dostupa ob座asnyaetsya v $$R.11. K chlenam bazovogo klassa, esli tol'ko oni ne pereopredeleny v proizvodnom klasse, mozhno obrashchat'sya tak, kak budto oni yavlyayutsya chlenami proizvodnogo klassa. Govoryat, chto proizvodnyj klass nasleduet chleny bazovogo klassa. S pomoshch'yu operacii razresheniya oblasti vidimosti :: ($$R.5.1) k chlenu bazovogo klassa mozhno obrashchat'sya yavno. Takoe obrashchenie vozmozhno i v tom sluchae, kogda imya chlena bazovogo klassa pereopredeleno v proizvodnom klasse. Proizvodnyj klass sam mozhet vystupat' kak bazovyj pri kontrole dostupa, sm. $$R.11.2. Ukazatel' na proizvodnyj klass mozhet neyavno preobrazovyvat'sya v ukazatel' na odnoznachno opredelennyj i dostupnyj bazovyj klass ($$R.4.6). Ssylka na proizvodnyj klass mozhet neyavno preobrazovyvat'sya v ssylku na odnoznachno opredelennyj i dostupnyj bazovyj klass ($$R.4.7). Rassmotrim primer: class base { public: int a, b; }; class derived : public base { public: int b, c; }; void f() { derived d; d.a = 1; d.base::b = 2; d.b = 3; d.c = 4; base* bp = &d; // standartnoe preobrazovanie derived* v base* } Zdes' prisvaivayutsya znacheniya chetyrem chlenam d, a bp nastraivaetsya na d. Klass nazyvaetsya pryamym bazovym, esli on nahoditsya v spiske-bazovyh, i kosvennym bazovym, esli sam ne yavlyayas' pryamym bazovym, on sluzhit bazovym dlya odnogo iz klassov spiska-bazovyh. Otmetim, chto v oboznachenii imya-klassa :: imya konstrukciya, imya mozhet byt' imenem chlena kosvennogo bazovogo klassa. Takoe oboznachenie prosto ukazyvaet klass, v kotorom sleduet nachinat' poisk etogo imeni. Privedem primer: class A { public: void f(); } class B : public A { }; class C : public B { public: void f(); } void C::f() { f(); // vyzov f() iz C A::f(); // vyzov f() iz A B::f(); // vyzov f() iz A } Zdes' dvazhdy vyzyvaetsya A::f(), poskol'ku eto edinstvennaya funkciya f() v klasse B. Inicializaciya ob容ktov, predstavlyayushchih bazovye klassy, zadaetsya v konstruktorah, sm. $$R.12.6.2. R.10.1 Mnozhestvennye bazovye klassy Klass mozhet byt' proizvodnym po otnosheniyu k lyubomu chislu bazovyh klassov. Privedem primer: class A { /* ... */ }; class B { /* ... */ }; class C { /* ... */ }; class D : public A, public B, public C { /* ... */ }; Ispol'zovanie bolee, chem odnogo pryamogo bazovogo klassa nazyvaetsya mnozhestvennym nasledovaniem. Poryadok nasledovaniya ne vazhen, esli ne uchityvat' voprosov, svyazannyh so standartnoj inicializaciej s pomoshch'yu konstruktora ($$R.12.1), unichtozheniem ($$R.12.4) i razmeshcheniem v pamyati ($$r.5.4, $$R.9.2, $$R.11.1). Poryadok vydeleniya pamyati dlya bazovyh klassov opredelyaetsya realizaciej. Nel'zya ukazyvat' klass v kachestve pryamogo bazovogo po otnosheniyu k proizvodnomu klassu bolee odnogo raza, no kosvennym bazovym klassom on mozhet byt' neodnokratno. class B { /* ... */ }; class D : public B, public B { /* ... */ }; // nedopustimo class L { /* ... */ }; class A : public L { /* ... */ }; class B : public L { /* ... */ }; class C : public A, public B { /* ... */ }; // normal'no Zdes' ob容kt klassa C budet imet' dva vlozhennyh ob容kta klassa L. K specifikacii bazovogo klassa mozhno dobavit' sluzhebnoe slovo virtual. Otdel'nyj ob容kt virtual'nogo bazovogo klassa V razdelyaetsya mezhdu vsemi bazovymi klassami, kotorye ukazali V pri zadanii svoih bazovyh klassov. Privedem primer: class V { /* ... */ }; class A : virtual public V { /* ... */ }; class B : virtual public V { /* ... */ }; class C : public A, public B { /* ... */ }; Zdes' ob容kt klassa C budet imet' tol'ko odin vlozhennyj ob容kt klassa V. Klass mozhet soderzhat' virtual'nye i nevirtual'nye bazovye klassy odnogo tipa, naprimer: class B { /* ... */ }; class X : virtual public B { /* ... */ }; class Y : virtual public B { /* ... */ }; class Z : public B { /* ... */ }; class AA : public X, public Y, public Z { /* ... */ }; Zdes' ob容kt klassa AA budet imet' dva vlozhennyh ob容kta klassa B: iz klassa Z i virtual'nyj, razdelyaemyj mezhdu klassami X i Y. R.10.1.1 Neodnoznachnosti Dostup k bazovomu klassu dolzhen byt' zadan odnoznachno. Dostup k chlenu bazovogo klassa schitaetsya neodnoznachnym, esli vyrazhenie, ispol'zuemoe dlya dostupa, zadaet bolee odnoj funkcii, ob容kta, tipa ili elementa perechisleniya. Proverka na odnoznachnost' proishodit do proverki vozmozhnosti dostupa ($$R.11). Privedem primer: class A { public: int a; int (*b)(); int f(); int f(int); int g(); }; class B { int a; int b(); public: int f(); int g(); int h(); int h(int); }; class C : public A, public B { }; void g(C* pc) { pc->a = 1; // oshibka: neodnoznachnost': A::a ili B::a pc->b(); // oshibka: neodnoznachnost': A::b ili B::b pc->f(); // oshibka: neodnoznachnost': A::f ili B::f pc->f(1); // oshibka: neodnoznachnost': A::f ili B::f pc->g(); // oshibka: neodnoznachnost': A::g ili B::g pc->g = 1; // oshibka: neodnoznachnost': A::g ili B::g pc->h(); // normal'no pc->h(1); // normal'no } Esli imya peregruzhennoj funkcii ustanovleno odnoznachno, to prezhde proverki vozmozhnosti dostupa proishodit eshche i razreshenie peregruzki. Neodnoznachnost' mozhno ustranit', utochnyaya ispol'zuemoe imya imenem klassa, naprimer, tak: class A { public: int f(); }; class B { public: int f(); }; class C : public A, public B { int f() { return A::f() + B::f(); } }; Esli ispol'zuyutsya virtual'nye bazovye klassy, do otdel'noj funkcii, ob容kta, tipa ili elementa perechisleniya mozhno dobrat'sya neskol'kimi putyami, dvigayas' po napravlennomu aciklichnomu grafu, kotoryj obrazuyut bazovye klassy. No eto ne yavlyaetsya neodnoznachnost'yu. Identichnoe zhe ispol'zovanie nevirtual'nyh bazovyh klassov porozhdaet neodnoznachnost', poskol'ku v etom sluchae uchastvuet v zadanii dostupa bolee odnogo vlozhennogo ob容kta. Privedem primer: class V { public: int v; }; class A { public: int a; }; class B : public A, public virtual V { }; class C : public A, public virtual V { }; class D : public B, public C { public: void f(); }; void D::f() { v++; // normal'no a++; // oshibka, neodnoznachnost': `a' v `D' vhodit dvazhdy } Esli ispol'zuyutsya virtual'nye bazovye klassy, vozmozhno chto dvigayas' po napravlennomu aciklichnomu grafu, mozhno dobrat'sya bolee, chem do odnogo imeni funkcii, ob容kta ili elementa perechisleniya. |to, konechno, neodnoznachnost', no krome sluchaya, kogda odno imya dominiruet nad drugimi. Identichnoe ispol'zovanie nevirtual'nyh bazovyh klassov vsegda privodit k neodnoznachnosti, t.k. v etom sluchae vsegda uchastvuet bolee odnogo vlozhennogo ob容kta. Schitaetsya, chto imya B::f dominiruet nad imenem A::f, esli klass A yavlyaetsya dlya klassa B bazovym. Esli odno imya dominiruet nad drugim, oni ne mogut privesti k neodnoznachnosti: v situacii vybora ispol'zuetsya vsegda dominiruyushchee imya. Privedem primer: class V { public: int f(); int x; }; class B : public virtual V { public: int f(); int x; }; class C : public virtual V { }; class D : public B, public C { void g(); }; void D::g() { x++; // normal'no: B::x dominiruet nad V::x f(); // normal'no: B::f() dominiruet nad V::f() } V rezul'tate yavnogo ili neyavnogo preobrazovaniya ukazatelya ili ssylki na proizvodnyj klass v ukazatel' ili ssylku na odin iz ego bazovyh klassov, eti ukazatel' ili ssylka dolzhny ukazyvat' tol'ko na tot zhe samyj ob容kt, kotoryj predstavlyaet bazovyj klass. Privedem primer: class V { }; class A { }; class B : public A, public virtual V { }; class C : public A, public virtual V { }; class D : public B, public C { }; void g() { D d; B* pb = &d; A* pa = &d; // oshibka, neodnoznachnost': A iz C ili A iz B? v* pv = &d; // normal'no: tol'ko odin vlozhennyj ob容kt V } R.10.2 Virtual'nye funkcii Esli klass base soderzhit virtual'nuyu ($$R.7.1.2) funkciyu vf, a proizvodnyj ot nego klass derived takzhe soderzhit funkciyu vf togo zhe tipa, togda vyzov vf dlya ob容kta klassa derived yavlyaetsya obrashcheniem k derived::vf, dazhe esli dostup k etoj funkcii proishodit cherez ukazatel' ili ssylku na klass base. Govoryat, chto funkciya proizvodnogo klassa podavlyaet funkciyu bazovogo klassa. Odnako, esli tipy funkcij ($$R.8.2.5) razlichny, funkcii schitayutsya raznymi i mehanizm virtual'nosti ne dejstvuet (sm. takzhe $$R.13.1). Schitaetsya oshibkoj, esli funkciya proizvodnogo klassa otlichaetsya ot virtual'noj funkcii bazovogo klassa tol'ko tipom vozvrashchaemogo znacheniya. Rassmotrim primer: struct base { virtual void vf1(); virtual void vf2(); virtual void vf3(); void f(); }; class derived : public base { public: void vf1(); void vf2(int); // skryvaet base::vf2() char vf3(); // oshibka: razlichie tol'ko v tipe // vozvrashchaemogo znacheniya } void g() { derived d; base* bp = &d; // standartnoe preobrazovanie: derived* v base* bp->vf1(); // vyzov derived::vf1 bp->vf2(); // vyzov base::vf2 bp->f(); // vyzov base::f } Zdes' tri vyzova dlya ob容kta d klassa derived privedut k obrashcheniyam k derived::vf1, base::vf2 i base::f sootvetstvenno. Inymi slovami, interpretaciya vyzova virtual'noj funkcii zavisit ot tipa ob容kta, dlya kotorogo ona vyzyvaetsya, togda kak interpretaciya vyzova nevirtual'noj funkcii-chlena zavisit tol'ko ot tipa ukazatelya ili ssylki na etot ob容kt. Naprimer, vyrazhenie bp->vf1() privedet k vyzovu derived::vf1(), poskol'ku bp ukazyvaet na ob容kt klassa derived, v kotorom funkciya derived::vf1() podavlyaet virtual'nuyu funkciyu base::vf1(). Nalichie specifikacii virtual oznachaet, chto funkciya yavlyaetsya chlenom, poetomu virtual'naya funkciya ne mozhet byt' global'noj funkciej (ne chlenom) ($$R.7.1.2). Tochno tak zhe virtual'naya funkciya ne mozhet byt' staticheskim chlenom, t.k. dlya vyzova virtual'noj funkcii neobhodimo nalichie opredelennogo ob容kta, kotoryj ukazyvaet, kakuyu funkciyu nado vyzyvat'. V drugom klasse virtual'nuyu funkciyu mozhno opisat' kak druga. Funkciya, podavlyayushchaya virtual'nuyu, sama schitaetsya virtual'noj funkciej. Specifikaciyu virtual mozhno ispol'zovat' dlya podavlyayushchej funkcii proizvodnogo klassa, no eto izbytochno. Virtual'naya funkciya mozhet byt' opredelena ili opisana v bazovom klasse kak chistaya ($$R.10.3). Virtual'nuyu funkciyu, kotoraya opredelena v bazovom klasse, ne nuzhno opredelyat' v proizvodnom klasse: pri vseh vyzovah budet ispol'zovat'sya funkciya, opredelennaya v bazovom klasse. Mehanizm virtual'nosti pri vyzove otklyuchaetsya, esli est' yavnoe utochnenie imeni s pomoshch'yu operatora razresheniya oblasti vidimosti ($$R.5.1), naprimer: class B { public: virtual void f(); }; class D : public B { public: void f(); }; void D::f() { /* ... */ B::f(); } Zdes' obrashchenie k f iz D privodit k vyzovu B::f, a ne D::f. R.10.3 Abstraktnye klassy Abstraktnye klassy dayut sredstvo dlya predstavleniya v yazyke obshchih ponyatij, takih, naprimer, kak figura, dlya kotoryh mogut ispol'zovat'sya tol'ko konkretnye ih varianty, naprimer, krug ili kvadrat. Krome togo abstraktnyj klass pozvolyaet zadat' interfejs, raznoobraznye realizacii kotorogo predstavlyayut proizvodnye klassy. Abstraktnym nazyvaetsya klass, kotoryj mozhno ispol'zovat' tol'ko kak bazovyj dlya nekotorogo drugogo klassa, t.e. nel'zya sozdat' nikakogo ob容kta abstraktnogo klassa krome togo, kotoryj predstavlyaet bazovyj klass dlya nekotorogo proizvodnogo klassa. Klass schitaetsya abstraktnym, esli v nem est' hotya by odna chistaya virtual'naya funkciya. Pri opisanii klassa virtual'naya funkciya opisyvaetsya kak chistaya s pomoshch'yu specifikacii-chistoj ($$R.9.2). CHistuyu virtual'nuyu funkciyu ne nuzhno opredelyat', esli tol'ko ona yavno ne vyzyvaetsya s pomoshch'yu konstrukcii utochnennoe-imya ($$R.5.1). Rassmotrim primer: class point { /* ... */ }; class shape { // abstraktnyj klass point center; // ... public: point where() { return center; } void move(point p) { center=p; draw(); } virtual void rotate(int) = 0; // chistaya virtual'naya virtual void draw() = 0; // chistaya virtual'naya // ... }; Abstraktnyj klass nel'zya ispol'zovat' kak tip formal'nogo parametra, tip vozvrashchaemogo znacheniya, a takzhe kak tip v operacii yavnogo preobrazovaniya tipa. Mozhno opisyvat' ukazateli i ssylki na abstraktnyj klass, naprimer: shape x; // oshibka: ob容kt abstraktnogo klassa shape* p; // normal'no shape f(); // oshibka void g(shape); // oshibka shape& h(shape&); // normal'no CHistye virtual'nye funkcii i nasleduyutsya kak chistye virtual'nye funkcii, naprimer: class ab_circle : public shape { int radius; public: void rotate(int) { } // ab_circle::draw() chistaya virtual'naya funkciya }; Poskol'ku funkciya shape::draw() yavlyaetsya chistoj virtual'noj funkciej, to takoj zhe budet po opredeleniyu i funkciya ab_circle::draw(). Dlya privedennogo nizhe opisaniya klass circle ne budet abstraktnym, i u funkcii circle::draw() gde-to dolzhno sushchestvovat' opredelenie. class circle : public shape { int radius: public: void rotate(int) { } void draw(); // dolzhna byt' gde-to opredelena }; Funkcii-chleny mozhno vyzyvat' iz konstruktora abstraktnogo klassa, rezul'tat pryamogo ili kosvennogo vyzova chistoj virtual'noj funkcii dlya ob容kta, sozdannogo s pomoshch'yu takogo konstruktora, neopredelen. R.10.4 Svodka pravil oblasti vidimosti Teper' mozhno svesti voedino pravila oblastej vidimosti dlya programmy na S++. |ti pravila odinakovo primenimy dlya vseh imen (vklyuchaya imya-typedef ($$R.7.1.3) i imya-klassa ($$R.9.1)) i v lyubom kontekste, dlya kotorogo oni dopustimy po sintaksisu yazyka. Zdes' rassmatrivayutsya tol'ko oblasti vidimosti na leksicheskom urovne, voprosy svyazyvaniya obsuzhdayutsya v $$R.3.3. Ponyatie momenta opisaniya bylo vvedeno v $$R.3.2. Vsyakoe ispol'zovanie imeni dolzhno byt' odnoznachnym (ne schitaya peregruzki) v oblasti ego vidimosti ($$R.10.1.1). Pravila dostupa ($$R.11) nachinayut dejstvovat' tol'ko togda, kogda imya mozhno odnoznachno najti v oblasti ego vidimosti. Tol'ko pri uslovii, chto prava dostupa k imeni ne narusheny, nachinaetsya proverka tipa ob容kta, funkcii ili elementa perechisleniya. Imya, kotoroe ispol'zuetsya vne lyuboj funkcii ili klassa, ili pered kotorym stoit unarnaya operaciya razresheniya oblasti vidimosti :: (i kotoroe ne utochnyaetsya binarnoj operaciej :: ili operaciyami -> ili .), dolzhno byt' imenem global'nogo ob容kta, ili funkcii, ili elementa perechisleniya, ili tipa. Imya, zadavaemoe posle X:: ili obj., gde obj tipa X ili tipa ssylka na X, a takzhe imya, zadavaemoe posle ptr->, gde ptr tipa ukazatel' na X, dolzhno byt' imenem chlena klassa X ili chlenom bazovogo po otnosheniyu k X klassa. Pomimo etogo, v obrashchenii ptr->imya ptr mozhet byt' ob容ktom klassa Y, v kotorom est' funkciya operator->(), opisannaya takim obrazom, chto ptr->operator() v konechnom schete okazyvaetsya ukazatelem na X ($$R.13.4.6). Imya, kotoroe ne utochnyaetsya odnim iz opisannyh vyshe sposobov, i, kotoroe ispol'zuetsya v funkcii, ne yavlyayushchejsya chlenom klassa, dolzhno byt' opisano v tom bloke, gde ono ispol'zuetsya, ili v ob容mlyushchem bloke ili dolzhno byt' global'nym. Opisanie lokal'nogo imeni skryvaet opisaniya togo zhe imeni v ob容mlyushchih blokah, a takzhe ego opisaniya kak global'nogo imeni. V chastnosti, peregruzka imeni nevozmozhna dlya imen v raznyh oblastyah vidimosti ($$R.13.4). Imya, kotoroe ne utochnyaetsya odnim iz opisannyh vyshe sposobov, i, kotoroe ispol'zuetsya v funkcii, yavlyayushchejsya nestaticheskim chlenom klassa X, dolzhno byt' opisano ili v tom bloke, gde ono ispol'zuetsya, ili v ob容mlyushchem bloke, i ono dolzhno byt' chlenom klassa X, ili chlenom bazovogo po otnosheniyu k X klassa, ili eto imya dolzhno byt' global'nym. Opisanie lokal'nyh imen skryvaet opisanie etih zhe imen v ob容mlyushchih blokah, v chlenah klassa etoj funkcii i sredi global'nyh imen. Opisanie chlena skryvaet analogichnye opisanie s tem zhe imenem v bazovyh klassah i sredi global'nyh imen. Imya, kotoroe ne utochnyaetsya odnim iz opisannyh vyshe sposobov, i, kotoroe ispol'zuetsya v staticheskoj funkcii-chlene klassa X, dolzhno byt' opisano ili v tom bloke, gde ono ispol'zuetsya, ili v ob容mlyushchem bloke, i dolzhno byt' staticheskim chlenom klassa X, ili bazovogo po otnosheniyu k X klassa, ili ono dolzhno byt' global'nym imenem. Imya formal'nogo parametra funkcii, zadannoe pri ee opredelenii ($$R.8.3), prinadlezhit oblasti vidimosti, sovpadayushchej s naibol'shim blokom funkcii (v chastnosti, yavlyaetsya lokal'nym imenem). Imya formal'nogo parametra funkcii, zadannoe v ee opisanii ($$R.8.2.5), a ne opredelenii, prinadlezhit lokal'noj oblasti vidimosti, kotoraya ischezaet srazu zhe posle opisaniya funkcii. Standartnye znacheniya parametrov nahodyatsya v oblasti vidimosti, opredelyaemoj v moment opisaniya ($$R.3.2) formal'nyh parametrov funkcii; v nih ne dolzhny ispol'zovat'sya lokal'nye peremennye ili nestaticheskie chleny klassa, i oni vychislyayutsya pri kazhdom vyzove funkcii ($$R.8.2.6). Inicializator-ctor ($$R.12.6.2) vychislyaetsya v oblasti vidimosti naibol'shego bloka konstruktora, dlya kotorogo on zadan. V chastnosti, v nem mozhno ispol'zovat' imena formal'nyh parametrov. R.11 Kontrol' dostupa k chlenam CHlen klassa mozhet byt': chastnym (private); eto znachit, chto ego imya mozhno ispol'zovat' tol'ko v funkciyah-chlenah i druz'yah klassa, v kotorom on opisan; zashchishchennym (protected); eto znachit, chto ego imya mozhno ispol'zovat' tol'ko v funkciyah-chlenah i druz'yah klassa, v kotorom on opisan, a takzhe v funkciyah-chlenah i druz'yah klassov, yavlyayushchihsya proizvodnymi po otnosheniyu k etomu klassu (sm. $$R.11.5); obshchim (public); eto znachit, chto ego imya mozhno ispol'zovat' v lyuboj funkcii. CHleny klassa, opisannogo so sluzhebnym slovom class, yavlyayutsya chastnymi po opredeleniyu. CHleny klassa, opisannogo so sluzhebnym slovom struct ili union, yavlyayutsya obshchimi po opredeleniyu, naprimer: class X { int ; // X:: chastnyj po opredeleniyu }; struct S { int a; // S::a obshchij po opredeleniyu }; R.11.1 Specifikacii dostupa Opisaniya chlenov mogut byt' snabzheny specifikaciej dostupa ($$R.10): specifikaciya-dostupa : spisok-chlenov opt Specifikaciya-dostupa zadaet pravila dostupa k chlenam, kotorye dejstvuyut do konca zhizni klassa ili poka ne poyavitsya drugaya specifikaciya-dostupa, naprimer, class X { int a; // X::a chastnyj po opredeleniyu: uchityvaetsya 'class' public: int b; // X::b obshchij int c; // X::c obshchij }; Dopustimo lyuboe chislo specifikacij dostupa i zadavat' ih mozhno v lyubom poryadke, naprimer, struct S { int a; // S::a obshchij po opredeleniyu: uchityvaetsya `struct' protected: int b; // S::b zashchishchennyj private: int c; // S::c chastnyj public: int d; // S:: d obshchij }; Poryadok razmeshcheniya chlenov, predstavlyayushchih dannye, kotorye imeyut raznye specifikacii-dostupa, opredelyaetsya realizaciej ($$R.9.2). R.11.2 Specifikacii dostupa dlya bazovyh klassov Esli klass opisan kak bazovyj ($$r.10) po otnosheniyu k drugomu klassu s pomoshch'yu specifikacii dostupa public, to chleny so specifikaciej public ili protected iz bazovogo klassa yavlyayutsya sootvetstvenno chlenami s toj zhe specifikaciej dlya proizvodnogo klassa. Esli klass opisan kak bazovyj po otnosheniyu k drugomu s pomoshch'yu specifikacii dostupa private, to chleny so specifikaciej public ili protected iz bazovogo klassa yavlyayutsya chlenami so specifikaciej private dlya proizvodnogo klassa. CHastnye chleny bazovogo klassa ostayutsya nedostupnymi dazhe dlya proizvodnyh klassov, esli tol'ko dlya obespecheniya dostupa pri opisanii bazovogo klassa ne bylo ispol'zovano opisanie friend. Esli dlya bazovogo klassa ne ukazana specifikaciya-dostupa, to dlya proizvodnogo klassa, esli on opisan kak struct, predpolagaetsya specifikaciya public, a esli on opisan so sluzhebnym slovom class, to - specifikaciya private, naprimer: class B { /* ... */ }; class D1 : private B { /* ... */ }; class D2 : public B { /* ... */ }; class D3 : B { /* ... */ }; // `B' chastnyj po opredeleniyu struct D4 : public B { /* ... */ }; struct D5 : private B { /* ... */ }; struct D6 : B { /* ... */ }; // `B' chastnyj po opredeleniyu Zdes' klass yavlyaetsya obshchim (public) bazovym klassom dlya D2, D4 i D6 i chastnym (private) bazovym klassom dlya D1, D2 i D5. Opisanie bazovogo klassa kak private ne vliyaet na dostup k staticheskim chlenam bazovogo klassa. Odnako, esli pri obrashchenii k staticheskomu chlenu ispol'zuetsya ob容kt ili ukazatel', kotoryj nuzhno preobrazovyvat', to dejstvuyut obychnye pravila preobrazovaniya ukazatelej. V funkciyah-chlenah ili druz'yah klassa X mozhno X* neyavno preobrazovyvat' v ukazatel' na chastnyj klass, yavlyayushchijsya neposredstvenno bazovym po otnosheniyu k X. R.11.3 Opisaniya dostupa Ispol'zuya utochnennoe imya, mozhno ustanovit' dostup k chlenu bazovogo klassa v chasti public ili protected opisaniya proizvodnogo klassa. |to nazyvaetsya opisaniem dostupa. Privedem primer: class B { int a; public: int b, c; int bf(); }; class D : private B { int d; public: B::c; // adjust access to `B::c' int e; int df(); }; int ef(D&); Vo vneshnej funkcii ef mozhno ispol'zovat' tol'ko imena c, e, i df. Poskol'ku funkciya df chlen klassa D, v nej mozhno ispol'zovat' imena b, c, bf, d, e i df, no ne a. Funkciya bf - chlen klassa B i v nej mozhno ispol'zovat' chleny a, b, c i bf. Opisaniya dostupa ne sleduet ispol'zovat' dlya ogranicheniya dostupa k chlenu, dostupnomu v bazovom klasse, takzhe kak ne sleduet ispol'zovat' ego dlya obespecheniya dostupa k chlenu, kotoryj nedostupen v bazovom klasse, naprimer: class B { public: int a; private: int b; protected: int c; }; class D : private B { public: B::a; // opisat' `a' kak obshchij chlen D B::b; // oshibka: popytka rasshirit' dostup, // `b' ne mozhet byt' obshchim chlenom D protected: B::c; // opisat' `c' kak zashchishchennyj chlen D B::a; // oshibka: popytka suzit' dostup, // `a' ne mozhet byt' zashchishchennym chlenom D }; Opisanie dostupa dlya imeni peregruzhennoj funkcii ustanavlivaet dostup v bazovom klasse ko vsem funkciyam s etim imenem, naprimer: class X { public: f(); f(int); }; class Y : private X { public: X::f; // makes X::f() and X::f(int) public in Y }; Nel'zya v proizvodnom klasse ustanovit' dostup k chlenu bazovogo klassa, esli v proizvodnom klasse opredelen chlen s etim zhe imenem, naprimer: class X { public: void f(); }; class Y : private X { public: void f(int); X::f; // oshibka: dva opisaniya f }; R.11.4 Druz'ya Drugom klassa nazyvaetsya funkciya, kotoraya ne yavlyaetsya chlenom klassa, no v kotoroj mozhno ispol'zovat' chastnye i zashchishchennye chleny etogo klassa. Imya druga ne prinadlezhit oblasti vidimosti klassa, i druzhestvennaya funkciya ne vyzyvaetsya s pomoshch'yu operacij dostupa k chlenam ($$R.5.2.4), esli tol'ko ona ne yavlyaetsya chlenom drugogo klassa. Sleduyushchij primer pokazyvaet razlichie mezhdu chlenami i druz'yami: class X { int a; friend void friend_set(X*, int); public: void member_set(int); }; void friend_set(X* p, int i) { p->a = i; } void X::member_set(int i) { a = i; } void f() { X obj; friend_set(&obj,10); obj.member_set(10); } Esli v opisanii friend ispol'zovano imya peregruzhennoj funkcii ili operacii, tol'ko funkciya, odnoznachno opredelyaemaya tipami formal'nyh parametrov, stanovitsya drugom. Funkciya-chlen klassa X mozhet byt' drugom klassa Y, naprimer: class Y { friend char* X::foo(int); // ... }; Mozhno ob座avit' vse funkcii klassa X druz'yami klassa Y s pomoshch'yu specifikacii-slozhnogo-tipa ($$R.9.1): class Y { friend class X; // ... }; Opisanie odnogo klassa kak drug drugogo klassa dopolnitel'no podrazumevaet, chto chastnye i zashchishchennye chleny klassa, predlagayushchego druzhbu, mogut ispol'zovat'sya v klasse, poluchayushchem ee, naprimer: class X { enum { a=100 }; friend class Y; }; class Y { int v[X::a]; // Y drug klassa X }; class Z { int v[X::a]; // oshibka: X::a nedostupno }; Esli klass ili funkciya, ob座avlennye kak druz'ya, ne byli opisany, ih imena popadayut v tu zhe oblast' vidimosti, chto i imya klassa, soderzhashchego opisanie friend ($$R.9.1). Funkciya, poyavivshayasya pervyj raz v opisanii friend, schitaetsya ekvivalentnoj funkcii, opisannoj kak extern ($$R.3.3, $$r.7.1.1). Esli funkciya-drug opredelena v opisanii klassa, ona schitaetsya funkciej so specifikaciej inline i k nej primenimo pravilo perenosa opredeleniya funkcii dlya funkcij-chlenov ($$R.9.3.2). Funkciya-drug, opredelennaya v opisanii klassa, otnositsya na leksicheskom urovne k oblasti vidimosti etogo klassa. Dlya funkcii-druga, opredelennoj vne klassa, eto ne tak. Na opisanie friend ne vliyaet ukazanie specifikacij-dostupa ($$R.9.2). Ponyatie druzhby ne yavlyaetsya ni nasleduemym, ni tranzitivnym. Podtverdim eto primerom: class A { friend class B; int a; }; class B { friend class C; }; class C { void f(A* p); { p->a++; // oshibka: C ne drug klassa A, hotya // yavlyaetsya drugom druga klassa A } }; class D : public B { void f(A* p) { p->a++; // oshibka: D ne drug klassa A, hotya // yavlyaetsya proizvodnym druga klassa A } }; R.11.5 Dostup k zashchishchennym chlenam Drug ili funkciya-chlen proizvodnogo klassa imeet dostup k zashchishchennomu staticheskomu chlenu bazovogo klassa. Drug ili funkciya-chlen proizvodnogo klassa mogut poluchit' dostup k zashchishchennomu nestaticheskomu chlenu odnogo iz svoih bazovyh klassov tol'ko cherez ukazatel', ssylku ili ob容kt proizvodnogo klassa (ili lyubogo klassa, yavlyayushchegosya proizvodnym po otnosheniyu k nemu). Rassmotrim primer: class B { protected: int i; }; class D1 : public B { }; class D2 : public B { friend void fr(B*, D1*, D2*); void mem(B*, D1*); }; void fr(B* pb, D1* p1, D2* p2) { pb->i = 1; // nedopustimo p1->i = 2; // nedopustimo p2->i = 3; // normal'no (obrashchenie cherez D2) } void D2::mem(B* pb, D1* p1) { pb->i = 1; // nedopustimo p1->i = 2; // nedopustimo i = 3; // normal'no (obrashchenie cherez this) } void g(B* pb, D1* p1, D2* p2) { pb->i = 1; // nedopustimo p1->i = 2; // nedopustimo p2->i = 3; // nedopustimo } R.11.6 Dostup k virtual'nym funkciyam Pravila dostupa ($$r.11) k virtual'noj funkcii opredelyayutsya ee opisaniem i na nih ne vliyayut pravila dostupa k k funkcii, kotoraya pozdnee budet podavlyat' ee. Privedem primer: class B { public: virtual f(); }; class D : public B { private: f(); }; void f() { D d; B* pb = &d; D* pd = &d; pb->f(); // normal'no: B::f() obshchij chlen // vyzyvaetsya D::f() pd->f(); // oshibka: D::f() chastnyj chlen } Prava dostupa proveryayutsya pri samom vyzove, ispol'zuya tip vyrazheniya, oboznachayushchee ob容kt, dlya kotorogo vyzyvaetsya funkciya-chlen (v primere vyshe eto B*). Dostup k funkcii-chlenu v klasse, gde ona opredelena (D v primere vyshe), v obshchem sluchae neizvesten. R.11.7 Mnozhestvennyj dostup Esli dobrat'sya do imeni mozhno neskol'kimi putyami po grafu, zadayushchemu mnozhestvennoe nasledovanie, to pravo dostupa etogo imeni schitaetsya maksimal'nym iz prav, poluchaemyh na raznyh putyah. Poyasnim eto primerom: class W { public: void f(); }; class A : private virtual W { }; class B : public virtual W { }; class C : public A, public B { void f() { W::f(); } // normal'no }; Poskol'ku W::f() dostupno v C::f() po puti, svyazannomu s obshchim nasledovaniem iz B, obrashchenie yavlyaetsya zakonnym. R.12 Special'nye funkcii-chleny Nekotorye funkcii-chleny schitayutsya special'nymi, poskol'ku oni vliyayut na to, kak ob容kty klassa sozdayutsya, kopiruyutsya i unichtozhayutsya, i kak znacheniya odnogo tipa preobrazuyutsya v znacheniya drugogo tipa. CHasto takie funkcii vyzyvayutsya neyavno. |ti funkcii-chleny podchinyayutsya obychnym pravilam dostupa ($$R.11). Naprimer, opisanie konstruktora so specifikaciej protected garantiruet, chto sozdavat' ob容kty s ego pomoshch'yu smogut tol'ko proizvodnye klassy i druz'ya. R.12.1 Konstruktory Konstruktorom nazyvaetsya funkciya-chlen, imya kotoroj sovpadaet s imenem klassa, on ispol'zuetsya dlya postroeniya znachenij, imeyushchih tip dannogo klassa. Esli v klasse est' konstruktor, to kazhdyj ob容kt etogo klassa pered proizvol'nym ispol'zovaniem budet inicializirovat'sya, sm. $$R.12.6. Konstruktor mozhet vyzyvat'sya dlya ob容kta so specifikaciej const ili volatile. Sam konstruktor nel'zya opisyvat' so specifikaciej const ili volatile ($$R.9.3.1). Konstruktor takzhe ne mozhet imet' specifikaciyu virtual ili static. Konstruktory ne nasleduyutsya, odnako, standartnye konstruktory i konstruktory kopirovaniya pri neobhodimosti sozdayutsya translyatorom ($$R.12.8). Takie konstruktory yavlyayutsya obshchimi. Standartnym konstruktorom dlya klassa X yavlyaetsya takoj konstruktor klassa X, kotoryj mozhno vyzyvat' bez parametrov. Standartnyj konstruktor dlya klassa X budet sozdan tol'ko togda, kogda dlya klassa X ne opisano ni odnogo konstruktora. Konstruktorom kopirovaniya dlya klassa X nazyvaetsya konstruktor, kotoryj vyzyvaetsya dlya kopirovaniya ob容kta klassa X, t.e. vyzyvaetsya s odnim parametrom tipa X. Naprimer, X::X(const X&) i X::X(X&, int=0) yavlyayutsya konstruktorami kopirovaniya. Konstruktor kopirovaniya sozdaetsya tol'ko togda, kogda ne opisano ni odnogo konstruktora kopirovaniya. Konstruktor kopirovaniya dlya klassa X ne dolzhen imet' v kachestve parametra ob容kt tipa X, naprimer X::X(X) nezakonnoe obrashchenie. Konstruktor dlya massiva elementov vyzyvaetsya v poryadke vozrastaniya adresov elementov ($$R.8.2.4). Esli u klassa est' bazovye klassy s konstruktorom ili chleny, yavlyayushchiesya ob容ktami s konstruktorom, ih konstruktory vyzyvayutsya prezhde, chem konstruktor proizvodnogo klassa. V $$R.12.6.2 ob座asnyaetsya kak zadayutsya parametry dlya takih konstruktorov i kak opredelyaetsya poryadok ih vyzova. Ob容kt klassa s konstruktorom ne mozhet byt' chlenom ob容dineniya. Dlya konstruktora ne nuzhno ukazyvat' nikakogo tipa vozvrashchaemogo znacheniya, dazhe void. V operatore return v tele konstruktora nel'zya ukazyvat' vozvrashchaemoe znachenie. Ne dopustima operaciya vzyatiya adresa konstruktora. Konstruktor mozhno yavno ispol'zovat' dlya sozdaniya ob容ktov ego tipa s pomoshch'yu sleduyushchej zapisi: imya-klassa ( spisok-vyrazhenij opt ) Privedem primer: complex zz = complex(1,2.3); print( complex(7.8,1.2) ); Ob容kt, sozdannyj takim obrazom yavlyaetsya bezymyannym (esli tol'ko konstruktor ne ispol'zovalsya dlya inicializacii poimenovannoj peremennoj kak zz vyshe), a vremya ego zhizni ogranicheno vyrazheniem, v kotorom on byl sozdan, sm. $$R.12.2. V konstruktore mozhno vyzyvat' funkciyu-chlen, sm. $$R.12.7. R.12.2 Vremennye ob容kty V nekotoryh situaciyah translyatoru byvaet neobhodimo ili udobno sozdavat' vremennye ob容kty. Ispol'zovanie vremennyh ob容ktov zavisit ot realizacii. Esli translyatoru ponadobilsya vremennyj ob容kt tipa klassa s konstruktorom, on dolzhen obespechit' vyzov konstruktora dlya etogo vremennogo ob容kta. Analogichno, neobhodimo vyzyvat' destruktor dlya ob容kta klassa, v kotorom opisan destruktor. Privedem primer: class X { // ... public: // ... X(int); X(X&); ~X(); }; X f(X); void g() { X a(1); X b = f(X(2)); a = f(b); } Zdes' nuzhen vremennyj ob容kt dlya postroeniya X(2), prezhde chem peredat' ego funkcii f() s pomoshch'yu X(X&). Al'ternativnoe reshenie, - postroit' ob容kt X(2) v pamyati, ispol'zuemoj dlya hraneniya parametra pri pervom vyzove f(). Pomimo etogo, vremennyj ob容kt mozhet ponadobit'sya dlya hraneniya rezul'tata f(X(2)) prezhde, chem kopirovat' ego v ob容kt b s pomoshch'yu X(X&), i zdes' vozmozhno al'ternativnoe reshenie: hranit' rezul'tat f(X(2)) v pamyati dlya ob容kta b. S drugoj storony, sushchestvuet mnogo funkcij f(), dlya kotoryh vypolnenie vyrazheniya a=f(a) trebuet vremennogo ob容kta ili dlya parametra a, ili dlya rezul'tata f(a), chtoby izbezhat' nezhelatel'nogo ispol'zovaniya pamyati, kotoroj pripisyvaetsya imya a. Translyator obyazan garantirovat' unichtozhenie vremennyh ob容ktov. Tochnyj moment unichtozheniya opredelyaetsya realizaciej. S vremennymi ob容ktami mozhno proizvodit' tol'ko dve operacii: vybrat' znachenie ob容kta (neyavno kopiruya ego) dlya ispol'zovaniya v drugom vyrazhenii, ili vzyat' ssylku na nego. Esli znachenie vremennogo ob容kta polucheno, on schitaetsya nenuzhnym i mozhet unichtozhat'sya nemedlenno. Esli na nego poluchena ssylka, to unichtozhat' ego nel'zya, poka sushchestvuet ssylka. Unichtozhenie dolzhno proizojti do vyhoda iz oblasti opredelennosti, v kotoroj byl sozdan vremennyj ob容kt. Drugoj vid vremennyh ob容ktov obsuzhdaetsya v $$R.8.4.3. R.12.3 Preobrazovaniya Preobrazovaniya ob容ktov klassa mozhno zadat' s pomoshch'yu konstruktorov ili funkcij preobrazovaniya. Takie preobrazovaniya, obychno nazyvaemye pol'zovatel'skimi, ispol'zuyutsya neyavno v sovokupnosti so standartnymi preobrazovaniyami ($$R.4). Naprimer, funkciyu s formal'nym parametrom tipa X mozhno vyzyvat' ne tol'ko s parametrom tipa X, no i parametrom tipa T, esli sushchestvuet preobrazovanie tipa T v X. Pol'zovatel'skie preobrazovaniya primenyayutsya v teh zhe situaciyah, chto i standartnye: preobrazovanie inicializatorov ($$R.8.4), parametrov funkcii ($$R.5.2.2), vozvrashchaemyh funkciej znachenij ($$R.6.6.3, $$R.8.2.5), vyrazhenij fakticheskih parametrov ($$R.5), vyrazhenij, upravlyayushchih ciklom i vyborom operatorov ($$R.6.4,$$R.6.5) i yavnye operacii preobrazovaniya tipa ($$R.5.2.3, $$R.5.4). Pol'zovatel'skie preobrazovaniya primenyayutsya tol'ko v sluchae ih odnoznachnosti ($$R.10.1.1, $$R.12.3.2). Preobrazovaniya prohodyat proverku na sootvetstvie pravilam dostupa ($$R.11). Kak vsegda proverka dostupa osushchestvlyaetsya posle razresheniya neodnoznachnosti ($$R.10.4). Primenenie preobrazovanij pri vyzove funkcii rassmatrivaetsya na primerah, privedennyh nizhe, a takzhe obsuzhdaetsya v $$R.13.2. R.12.3.1 Preobrazovanie s pomoshch'yu konstruktora Konstruktor, imeyushchij edinstvennyj parametr, zadaet preobrazovanie tipa svoego fakticheskogo parametra v tip ego klassa, naprimer: class X { // ... public: X(int); X(const char*, int = 0); }; void f(X arg) { X a = 1; // a = X(1); X b = "Jessie"; // b = X("Jessie",0) a = 2; // a = X(2) f(3); // f(X(3)) } Esli v klasse X net konstruktora, kotoryj dopuskaet zadannyj tip, ne delaetsya popytki najti kakoj-libo konstruktor drugogo klassa ili funkciyu preobrazovaniya dlya privedeniya zadannogo znacheniya v znachenie tipa,dopustimogo dlya konstruktora klassa X, naprimer: class X { /* ... *