/ X(int); }; class Y { /* ... */ Y(X); }; Y a = 1; // nedopustimo: preobrazovanie Y(X(1)) // ne primenyaetsya R.12.3.2 Funkcii preobrazovaniya Funkciya-chlen klassa X, imya kotoroj imeet vid, imya-funkcii-preobrazovaniya: operator imya-tipa-preobrazovaniya imya-tipa-preobrazovaniya: spisok-specifikacij-tipa opt operaciya-ptr opt zadaet preobrazovanie iz tipa X v tip, opredelyaemyj konstrukciej imya-tipa-preobrazovaniya. Takie funkcii-chleny nazyvayutsya funkciyami preobrazovaniya. V konstrukcii spisok-specifikacij-tipa nel'zya opisyvat' klassy, perechisleniya i imena-typedef, a takzhe nel'zya zadavat' tipy formal'nyh parametrov i tip vozvrashchaemogo znacheniya. Privedem primer: class X { // ... public: operator int(); }; void f(X a) { int i = int(a); i = (int)a; i = a; } Zdes' vo vseh treh operatorah prisvaivaemoe znachenie budet preobrazovyvat'sya s pomoshch'yu funkcii X::operator int(). Pol'zovatel'skie preobrazovaniya ne ogranichivayutsya tol'ko ispol'zovaniem v prisvaivanii i inicializacii, naprimer: void g(X a, X b) { int i = (a) ? 1+a : 0; int j = (a&&b) ? a+b : i; if (a) { // ... } } Operacii preobrazovaniya nasleduyutsya. Funkcii preobrazovaniya mogut byt' virtual'nymi. K dannomu znacheniyu neyavno primenyaetsya ne bolee odnogo pol'zovatel'skogo preobrazovaniya (s pomoshch'yu konstruktora ili funkcii preobrazovaniya), naprimer: class X { // ... public: operator int(); }; class Y { // ... public: operator X(); }; Y a; int b = a; // nedopustimo: preobrazovanie // a.operator X().operator int() ne primenyaetsya int c = X(a); // normal'no: a.operator X().operator int() Pol'zovatel'skie preobrazovaniya osushchestvlyayutsya neyavno tol'ko pri uslovii ih odnoznachnosti. Funkciya preobrazovaniya proizvodnogo klassa ne zakryvaet funkciyu preobrazovaniya bazovogo klassa, esli tol'ko oni ne preobrazuyut k odnomu i tomu zhe tipu, naprimer: class X { public: // ... operator int(); }; class Y : public X { public: // ... operator void*(); }; void f(Y& a) { if (a) { // oshibka: neodnoznachnost' } } R.12.4 Destruktory Destruktorom nazyvaetsya funkciya-chlen klassa cl s imenem ~cl, ona ispol'zuetsya dlya unichtozheniya znachenij tipa cl neposredstvenno pered unichtozheniem ob容kta, soderzhashchego ih. Destruktor ne imeet formal'nyh parametrov i dlya nego nel'zya zadat' tip vozvrashchaemogo znacheniya (dazhe void). Nel'zya primenyat' operaciyu vzyatiya adresa dlya destruktora. Mozhno vyzyvat' destruktor dlya ob容ktov so specifikaciej const ili volatile, no sam destruktor nel'zya opisyvat' s etimi specifikaciyami ($$R.9.3.1). Destruktor ne mozhet byt' i staticheskim. Destruktory ne nasleduyutsya. Esli bazovyj klass ili chlen imeyut destruktor, a sam proizvodnyj klass - net, to sozdaetsya standartnyj destruktor, kotoryj vyzyvaet destruktory bazovyh klassov i chlenov proizvodnogo klassa. Takie sozdannye destruktory imeyut specifikaciyu public. Telo destruktora vypolnyaetsya prezhde destruktorov dlya ob容ktov, yavlyayushchihsya chlenami. Destruktory dlya nestaticheskih ob容ktov, yavlyayushchihsya chlenami, vypolnyayutsya prezhde, chem destruktory dlya bazovyh klassov. Destruktory dlya nevirtual'nyh bazovyh klassov vypolnyayutsya prezhde, chem destruktory dlya virtual'nyh bazovyh klassov. Destruktory dlya nevirtual'nyh bazovyh klassov vypolnyayutsya v poryadke, obratnom ih opisaniyu v proizvodnom klasse. Destruktory virtual'nyh bazovyh klassov vypolnyayutsya v poryadke, obratnom poyavleniyu ih pri obhode snizu i sleva-napravo aciklichnogo napravlennogo grafa bazovyh klassov. Zdes' "sleva-napravo" oznachaet poryadok poyavleniya imen bazovyh klassov, kotoryj byl pri opisanii ih v proizvodnom klasse. Destruktory dlya elementov massiva vyzyvayutsya v poryadke, obratnom vyzovam pri ih postroenii. Destruktor mozhet byt' virtual'nym. V destruktore mozhno vyzyvat' funkciyu-chlen, sm. $$R.12.7. Ob容kt klassa s destruktorom ne mozhet byt' chlenom ob容dineniya. Destruktory vyzyvayutsya neyavno v sleduyushchih sluchayah: (1) kogda ischezayut iz oblasti vidimosti ob容kty auto ($$R.3.5) ili vremennye ob容kty ($$R.12.2, $$R.8.4.3); (2) pri zavershenii programmy ($$R.3.4) dlya postroennyh staticheskih ob容ktov ($$R.3.5); (3) blagodarya obrashcheniyu k operacii delete ($$R.5.3.4) dlya ob容ktov, sozdannyh s pomoshch'yu operacii new ($$R.5.3.3); (4) pri yavnom vyzove. Kogda destruktor vyzyvaetsya operaciej delete, to on osvobozhdaet pamyat' dlya samogo bol'shego iz proizvodnyh klassov ($$R.12.6.2) togo ob容kta, kotoryj ispol'zoval operaciyu delete() ($$R.5.3.4), naprimer: class X { // ... public: X(int); ~X(); }; void g(X*); void f() // obshchij sluchaj { X* p = new X(111); // razmeshchenie i inicializaciya g(p); delete p; // osvobozhdenie i udalenie } YAvnye vyzovy destruktorov primenyayutsya redko. Primerom etogo mozhet sluzhit' vyzov destruktora dlya ob容ktov, sozdannyh v nekotoroj opredelennoj adresnoj oblasti s pomoshch'yu operacii new. Razmeshchenie ob容ktov v opredelennom adresnom prostranstve i posleduyushchee unichtozhenie ih mozhet potrebovat'sya dlya ispol'zovaniya specificheskih vozmozhnostej apparatury i dlya pravil'nogo funkcionirovaniya operativnoj pamyati. Privedem primer: void* operator new(size_t, void* p) { return p; } void f(X* p); static char buf[sizeof(X)]; void g() // redkij, special'nyj sluchaj { X* p = new(buf) X(222); // razmeshchenie v buf[] i inicializaciya f(p); p->X::~X(); // udalenie } Oboznacheniya, ispol'zovannye dlya yavnogo vyzova destruktora, mozhno ispol'zovat' dlya imeni lyubogo prostogo tipa, naprimer, int* p; // ... p->int::~int(); Ispol'zovanie takoj zapisi dlya tipa, u kotorogo net destruktora, prohodit bessledno. Dopuskaya takuyu zapis', my razreshaem pol'zovatelyam pisat' programmu, ne zadumyvayas' nad tem, est' li dannogo tipa destruktor. R.12.5 Svobodnaya pamyat' Kogda sozdaetsya ob容kt s pomoshch'yu operacii new, dlya polucheniya svobodnoj pamyati vyzyvaetsya (neyavno) funkciya operator new() ($$R.5.3.3). Esli funkciya operator new() ne mozhet vypolnit' zapros, ona vozvrashchaet 0. V klasse X funkciya X::operator new() yavlyaetsya staticheskim chlenom, dazhe esli ona ne opisana yavno kak static. Pervyj ee parametr dolzhen imet' tip size_t, - zavisyashchij ot realizacii celochislennyj tip, kotoryj opredelen v standartnom zagolovochnom fajle <stddef.h>, i ona dolzhna vozvrashchat' znachenie tipa void*, naprimer: class X { // ... void* operator new(size_t); void* operator new(size_t, Arena*); }; Pravila vybora podhodyashchej funkcii operator new() obsuzhdayutsya v $$R.5.3.3. V klasse X funkciya X::operator delete() yavlyaetsya staticheskim chlenom, dazhe esli ona ne opisana yavno kak static. Pervyj ee parametr dolzhen byt' tipa void* i mozhno dobavlyat' vtoroj parametr tipa size_t. Ona ne mozhet vozvrashchat' kakoe-libo znachenie i tip vozvrashchaemogo znacheniya dolzhen byt' void, naprimer: class X { // ... void operator delete(void*); }; class Y { // ... void operator delete(void*, size_t); }; V kazhdom klasse mozhno opisat' tol'ko odnu funkciyu operator delete(), znachit eta funkciya ne mozhet byt' peregruzhennoj. Global'naya funkciya operator delete() imeet edinstvennyj parametr tipa void*. Esli funkciya opisana s dvumya formal'nymi parametrami, ona vyzyvaetsya s dvumya parametrami, vtoroj iz kotoryh pokazyvaet razmer udalyaemogo ob容kta. Peredavaemyj razmer opredelyaetsya s pomoshch'yu destruktora (esli on est') ili po tipu (staticheskomu) ukazatelya na udalyaemyj ob容kt. Operaciya projdet korrektno, esli tip ukazatelya, zadannogo kak fakticheskij parametr, budet sovpadat' s tipom ob容kta (a ne budet, k primeru, prosto tipom ukazatelya na bazovyj klass) ili, esli etot tip yavlyaetsya tipom ukazatelya na bazovyj klass s virtual'nym destruktorom. Dlya massivov ob容ktov tipa klass ispol'zuyutsya global'nye funkcii operator new() i operator delete() ($$R.5.3.3, $$R.5.3.4). Poskol'ku funkcii X::operator new() i X::operator delete() staticheskie, oni ne mogut byt' virtual'nymi. Funkciya operator delete(), kotoraya vyzyvaetsya iz destruktora dlya osvobozhdeniya pamyati, vybiraetsya po obychnym pravilam oblastej vidimosti, naprimer: struct B { virtual ~B(); void* operator new(size_t); void operator delete(void*); }; struct D : B { ~D(); void* operator new(size_t); void operator delete(void*); }; void f() { B* p = new D; delete p; } V etom primere pamyat' dlya ob容kta klassa D vydelyaetsya s pomoshch'yu D::operator new(), a blagodarya nalichiyu virtual'nogo destruktora, osvobozhdaetsya s pomoshch'yu D::operator delete(). R.12.6 Inicializaciya Ob容kt klassa bez konstruktorov, bez chastnyh ili zashchishchennyh chlenov, bez virtual'nyh funkcij i bez bazovyh klassov mozhno inicializirovat' s pomoshch'yu spiska inicializatorov ($$R.8.4.1). Ob容kt klassa s konstruktorom dolzhen inicializirovat'sya ili imet' standartnyj konstruktor ($$R.12.1). Standartnyj konstruktor ispol'zuetsya dlya ob容ktov, kotorye ne prohodyat yavnoj inicializacii. R.12.6.1 YAvnaya inicializaciya Ob容kty klassov s konstruktorami ($$R.12.1) mozhno inicializirovat' spiskom vyrazhenij, zaklyuchennym v skobki. |tot spisok schitaetsya spiskom fakticheskih parametrov dlya vyzova konstruktora, proizvodyashchego inicializaciyu. Inache, v kachestve inicializatora zadaetsya s pomoshch'yu operacii = odno znachenie. Ono ispol'zuetsya kak fakticheskij parametr dlya konstruktora kopirovaniya. Obychno mozhno obojtis' bez vyzova konstruktora kopirovaniya, naprimer: class complex { // ... public: complex(); complex(double); complex(double,double); // ... }; complex sqrt(complex,complex); complex a(1); // inicializaciya vyzovom // complex(double) complex b = a; // inicializaciya kopirovaniem `a' complex c = complex(1,2); // konstruktor complex(1,2) // vyzyvaetsya complex(double,double) // i kopiruetsya v `c' complex d = sqrt(b,c); // vyzyvaetsya sqrt(complex,complex), // rezul'tat kopiruetsya v `d' complex e; // inicializaciya vyzovom konstruktora complex f = 3; // complex(3), vyzyvaetsya // complex(double) i rezul'tat // kopiruetsya v `f' Peregruzka operacii prisvaivaniya = ne okazyvaet vliyanie na inicializaciyu. Inicializaciya, proishodyashchaya pri peredache fakticheskih parametrov i pri vozvrate iz funkcii, ekvivalentna inicializacii vida T x = a; Inicializaciya, proishodyashchaya v vyrazhenii operacii new ($$R.5.3.3) i pri inicializacii bazovyh klassov i chlenov, ekvivalentna inicializacii vida T x(a); Dlya massivov ob容ktov klassa s konstruktorami ispol'zuyutsya pri inicializacii ($$R.12.1) konstruktory kak i dlya odinochnyh ob容ktov. Esli okazalos', chto inicializatorov v spiske men'she, chem elementov massiva, ispol'zuetsya standartnyj konstruktor ($$R.12.1). Esli ego net, spisok inicializatorov dolzhen byt' polnym. Privedem primer: complex cc = { 1, 2 }; // oshibka: neobhodimo // ispol'zovat' konstruktor complex v[6] = { 1,complex(1,2),complex(),2 }; Zdes' v[0] i v[3] inicializiruyutsya znacheniem complex::complex(double), v[1] inicializiruetsya complex::complex(double,double), a v[2], v[4] i v[5] inicializirovany complex::complex(). Ob容kt klassa M moet byt' chlenom klassa X v odnom iz sleduyushchih sluchaev: (1) M ne imeet konstruktora; (2) M imeet standartnyj konstruktor; (3) X imeet konstruktor i kazhdyj iz nih zadaet inicializator-ctor ($$R.12.6.2) dlya chlena M. V sluchae 2 pri sozdanii sostavnogo ob容kta vyzyvaetsya standartnyj konstruktor. Esli chlen sostavnogo ob容kta imeet destruktor, to on vyzyvaetsya pri unichtozhenii sostavnogo ob容kta. Konstruktory dlya nelokal'nyh staticheskih ob容ktov vyzyvayutsya v tom poryadke, v kakom oni idut v tekste programmy, destruktory vyzyvayutsya v obratnom poryadke, sm. takzhe $$R.3.4, $$R.6.7, $$R.9.4. R.12.6.2 Inicializaciya chlenov i bazovyh klassov V opredelenii konstruktora mozhno zadat' inicializaciyu pryamyh bazovyh klassov i chlenov, ne nasleduemyh iz bazovyh klassov. |to osobenno polezno dlya teh ob容ktov, konstant i ssylok, dlya kotoryh razlichayutsya semantiki prisvaivaniya i inicializacii. Konstrukciya inicializator-ctor imeet vid inicializator-ctor: : spisok-inicializatorov-chlenov spisok-inicializatorov-chlenov: inicializator-chlena inicializator-chlena , spisok-inicializatorov-chlena inicializator-chlena: polnoe-imya-klassa ( spisok-vyrazhenij opt ) identifikator Spisok parametrov ispol'zuetsya dlya inicializacii nestaticheskih chlenov ili ob容ktov bazovogo klassa. |to edinstvennyj sposob inicializacii nestaticheskih chlenov, yavlyayushchihsya ssylkami ili ob容ktami tipa const, naprimer: struct B1 { B1(int); /* ... */ }; struct B2 { B2(int); /* ... */ }; struct D : B1, B2 { D(int); B1 b; const c; }; D::D(int a) : B2(a+1), B1(a+2), c(a+3), b(a+4) { /* ... */ } D d(10); V nachale inicializiruyutsya bazovye klassy v poryadke ih opisaniya (nezavisimo ot poryadka inicializatorov-chlenov), zatem po toj zhe sheme inicializiruyutsya chleny, i nakonec vypolnyaetsya telo D::D() ($$R.12.1). Poryadok opisaniya vyderzhivaetsya dlya togo, chtoby garantirovat', chto vlozhennye ob容kty i chleny budut unichtozhat'sya v poryadke, obratnom ih inicializacii. Osobyj sluchaj predstavlyayut virtual'nye bazovye klassy. Oni sozdayutsya prezhde, chem lyuboj nevirtual'nyj bazovyj klass i v tom zhe poryadke, v kakom poyavlyayutsya pri obhode snizu i sleva-napravo aciklichnogo napravlennogo grafa bazovyh klassov. Poryadok "sleva-napravo" - eto tot, v kotorom imena bazovyh klassov zadayutsya pri opisanii v proizvodnom klasse. Polnym nazyvaetsya ob容kt, kotoryj ne yavlyaetsya vlozhennym ob容ktom, predstavlyayushchim nekotoryj bazovyj klass. Klass takogo ob容kta nazyvayut naibol'shim proizvodnym klassom ob容kta. Vse vlozhennye ob容kty virtual'nyh bazovyh klassov inicializiruyutsya s pomoshch'yu konstruktora naibol'shego proizvodnogo klassa. Esli v konstruktore naibol'shego proizvodnogo klassa ne zadan inicializator-chlena dlya virtual'nogo bazovogo klassa, togda etot virtual'nyj bazovyj klass dolzhen imet' standartnyj konstruktor,libo ne imet' nikakogo konstruktora. Vsyakij inicializator-chlena dlya virtual'nogo bazovogo klassa, zadannyj ne v konstruktore klassa polnogo ob容kta, ignoriruetsya. Privedem primer: class V { public: V(); V(int); // ... }; class A : public virtual V { public: A(); A(int); // ... }; class B : public virtual V { public: B(); B(int); // ... }; class C : public A, public B, private virtual V { public: C(); C(int); // ... }; A::A(int i) : V(i) { /* ... */ } B::B(int i) { /* ... */ } C::C(int i) { /* ... */ } V v(1); // use V(int) A a(2); // use V(int) B b(3); // use V() C c(4); // use V() Inicializator-chlena vychislyaetsya v oblasti vidimosti konstruktora, v kotorom on poyavilsya. Naprimer, v sleduyushchem fragmente class X { int a; public: const int& r; X()::r(a) { } }; X::r inicializiruetsya dlya kazhdogo ob容kta klassa X ssylkoj na X::a. R.12.7 Konstruktory i destruktory V konstruktorah i destruktorah mozhno vyzyvat' funkciyu-chlen. Otsyuda sleduet, chto mozhno vyzyvat' (neposredstvenno ili kosvenno) virtual'nye funkcii. Vyzyvaemaya funkciya dolzhna byt' opredelena v klasse samogo konstruktora ili destruktora ili v bazovyh klassah, no ne dolzhna byt' funkciej, kotoraya ih podavlyaet v proizvodnom klasse. |tim obespechivaetsya to, chto eshche nesozdannye ob容kty ne budut ispol'zovany pri vypolnenii konstruktora ili destruktora. Rassmotrim primer: class X { public: virtual void f(); X() { f(); } // vyzov X::f() ~X() { f(); } // vyzov X::f() }; class Y : public X { int& r; public: void f() { r++; // beda, esli `r' ne inicializirovano } Y(int& rr) ::r(rr) { } }; Rezul'tat neposredstvennogo ili kosvennogo vyzova iz konstruktora chistoj virtual'noj funkcii dlya inicializiruemogo ob容kta neopredelen, esli tol'ko yavno ne ispol'zovano utochnenie imeni funkcii ($$R.10.3). R.12.8 Kopirovanie ob容ktov klassa Ob容kty klassa mogut kopirovat'sya dvumya sposobami: libo prisvaivaniem ($$R.5.17), libo inicializaciej ($$R.12.1, $$R.8.4), kotoraya mozhet proishodit' pri peredache parametrov ($$R.5.2.2) ili rezul'tata funkcii ($$R.6.6.3). Dlya klassa X eti dve operacii konceptual'no realizuyutsya kak operaciya prisvaivaniya i konstruktor kopirovaniya ($$R.12.1). V programme mozhno opredelit' ili odnu iz nih, ili obe. Esli pol'zovatel' ne opredelil ih v programme, to oni budut dlya vseh chlenov klassa X opredelyat'sya sootvetstvenno kak prisvaivanie po chlenam i inicializaciya po chlenam. Esli vse bazovye klassy i vse chleny klassa X imeyut konstruktor kopirovaniya, v kotorom dopustimy v kachestve parametra ob容kty tipa const, to porozhdaemyj konstruktor kopirovaniya dlya X budet imet' edinstvennyj parametr tipa const X& i zapisyvat'sya tak: X::X(const X&) Inache, u nego budet edinstvennyj parametr tipa X&: X::X(X&) i inicializaciya kopirovaniem ob容ktov tipa const klassa X budet nevozmozhna. Analogichno, esli vse bazovye klassy i chleny klassa X imeyut operaciyu prisvaivaniya, dopuskayushchuyu parametry tipa const, togda porozhdaemaya dlya X operaciya prisvaivaniya budet imet' edinstvennyj parametr tipa const X& i zapisyvat'sya tak: X& X::operator=(const X&) Inache, u nee budet edinstvennyj parametr tipa X&: X& X::operator=(X&) i prisvaivanie kopirovaniem ob容ktov klassa X tipa const budet nevozmozhno. Standartnaya operaciya prisvaivaniya vozvrashchaet ssylku na ob容kt, kotoryj nuzhno bylo kopirovat'. Ob容kty, predstavlyayushchie virtual'nye bazovye klassy, budut inicializirovat'sya tol'ko odin raz s pomoshch'yu porozhdaemogo konstruktora kopirovaniya. Ob容kty, predstavlyayushchie virtual'nye bazovye klassy, dopuskayut prisvaivaniya im tol'ko odin raz s pomoshch'yu porozhdaemoj operacii prisvaivaniya. Prisvaivanie po chlenam i inicializaciya po chlenam oznachayut sleduyushchee: esli klass X imeet v kachestve chlena klass M, dlya realizacii prisvaivaniya i inicializacii chlena ispol'zuyutsya operacii prisvaivaniya v M i konstruktor kopirovaniya M sootvetstvenno. Esli klass imeet chlen tipa const, ili chlen, yavlyayushchijsya ssylkoj, ili chlen ili bazovyj klass takogo klassa, gde funkciya operator=() yavlyaetsya chastnoj, to dlya nego standartnaya operaciya prisvaivaniya ne mozhet byt' sozdana. Analogichno, esli chlen ili bazovyj klass klassa M imeet chastnyj konstruktor kopirovaniya, to standartnyj konstruktor kopirovaniya dlya takogo klassa ne mozhet byt' sozdan. Poka ne poyavitsya neobhodimost' v opredelenii, standartnye prisvaivanie i konstruktor kopirovaniya budut tol'ko opisany (t.e. ne budet sozdano telo funkcii). Inymi slovami, funkciya X::operator=() budet porozhdena tol'ko togda, kogda net yavnogo opisaniya operacij prisvaivaniya, a ob容kt klassa X prisvaivaetsya ob容ktu klassa X ili ob容ktu klassa, proizvodnogo ot X, ili vychislyaetsya adres funkcii X::operator=(). Analogichnaya situaciya s inicializaciej. Esli prisvaivanie i konstruktor kopirovaniya opisany neyavno, to oni budut obshchimi funkciyami-chlenami i operaciya prisvaivaniya dlya klassa X opredelyaetsya takim obrazom, chto ee rezul'tatom yavlyaetsya ssylka tipa X& na ob容kt, kotoromu prisvaivayut. Esli v klasse X est' funkciya X::operator=(), parametrom kotoroj yavlyaetsya sam klass X, to standartnoe prisvaivanie ne budet porozhdat'sya. Esli v klasse opredelen kakoj-libo konstruktor kopirovaniya, to standartnyj konstruktor kopirovaniya ne budet porozhdat'sya. Privedem primer: class X { // ... public: X(int); X(const X&, int = 1); }; X a(1); // vyzov X(int) X b(a,0); // vyzov X(const X&,int) X c = b; // vyzov X(const X&,int) Prisvaivanie ob容ktov klassa X opredelyaetsya cherez funkciyu X::operator=(const X&). |to oznachaet ($$R.12.3), chto ob容kty proizvodnogo klassa mozhno prisvaivat' ob容ktam obshchego bazovogo klassa, naprimer: class X { public: int b; }; class Y : public X { public: int c; }; void f() { X x1; Y y1; x1 = y1; // normal'no y1 = x1; // oshibka } V etom primere y1.b prisvaivaetsya x1.b, a x1.c ne kopiruetsya. Kopirovanie odnogo ob容kta v drugoj s pomoshch'yu standartnoj operacii kopirovaniya ili standartnogo konstruktora kopirovaniya ne izmenyaet strukturu oboih ob容ktov. Privedem primer: struct s { virtual f(); // ... }; struct ss : public s { f(); // ... }; void f() { s a; ss b; a = b; // na samom dele vypolnyaetsya a.s::operator=(b) b = a; // oshibka a.f(); // vyzov s::f b.f(); // vyzov ss::f (s&)b = a; // prisvaivanie a b // na samom dele vypolnyaetsya ((s&)b).s::operator=(a) b.f(); // vse eshche vyzov ss::f } Vyzov a.f() privedet k vyzovu s::f() (kak i dolzhno byt' dlya ob容kta klassa s ($$R.10.2)), a vyzov b.f() privedet k vyzovu ss::f() ( kak i dolzhno byt' dlya ob容kta klassa ss). R.13 Peregruzka Govoryat, chto imya peregruzheno, esli dlya nego zadano neskol'ko razlichnyh opisanij funkcij v odnoj oblasti vidimosti. Pri ispol'zovanii imeni vybor pravil'noj funkcii proizvoditsya putem sopostavleniya tipov formal'nyh parametrov s tipami fakticheskih parametrov, naprimer: double abs(double); int abs(int); abs(1); // vyzov abs(int) abs(1.0); // vyzov abs(double) Poskol'ku pri lyubom tipe T i dlya samogo T , dlya i T& dopustimo odno i to zhe mnozhestvo inicializiruyushchih znachenij, funkcii, tipy parametrov kotoryh razlichayutsya tol'ko ispol'zovaniem, ili ne ispol'zovaniem ssylki, ne mogut imet' odinakovye imena, naprimer: int f(int i) { // ... } int f(int& r) // oshibka: tipy funkcij { // nedostatochno razlichny // ... } Analogichno, poskol'ku dlya lyubom tipe T dlya samogo T, const T i volatile T dopustimo odno i to zhe mnozhestvo inicializiruyushchih znachenij, funkcii, tipy parametrov kotoryh otlichayutsya tol'ko ukazannoj specifikaciej, ne mogut imet' odinakovye imena. Odnako, razlichit' const T&, volatile T& i prosto T& mozhno, poetomu dopustimy opredeleniya funkcij s odnim imenem, kotorye razlichayutsya tol'ko v ukazannom otnoshenii. Analogichno, dopustimy opredeleniya funkcij s odnim imenem, tipy parametrov kotoryh razlichayutsya tol'ko kak tipy vida const T*, volatile T* i prosto T*. Ne mogut imet' odinakovye imena funkcii, kotorye otlichayutsya tol'ko tipom vozvrashchaemogo znacheniya. Ne mogut imet' odinakovye imena funkcii-chleny, odna iz kotoryh staticheskaya, a drugaya net ($$R.9.4). S pomoshch'yu konstrukcii typedef ne sozdayutsya novye tipy, a tol'ko opredelyaetsya sinonim tipa ($$R.7.1.3), poetomu funkcii, kotorye otlichayutsya tol'ko za schet ispol'zovaniya tipov, opredelennyh s pomoshch'yu typedef, ne mogut imet' odinakovye imena. Privedem primer: typedef int Int; void f(int i) { /* ... */ } void f(Int i) { /* ... */ } // oshibka: pereopredelenie f S drugoj storony vse perechisleniya schitayutsya raznymi tipami, i s ih pomoshch'yu mozhno razlichit' peregruzhennye funkcii, naprimer: enum E { a }; void f(int i) { /* ... */ } void f(E i) { /* ... */ } Tipy parametrov, kotorye razlichayutsya tol'ko tem, chto v odnom ispol'zuetsya ukazatel' *, a v drugom massiv [], schitayutsya identichnymi. Napomnim, chto dlya tipa parametra vazhny tol'ko vtoroj i posleduyushchie indeksy mnogomernogo massiva ($$R.8.2.4). Podtverdim skazannoe primerom: f(char*); f(char[]); // identichno f(char*); f(char[7]); // identichno f(char*); f(char[9]); // identichno f(char*); g(char(*)[10]); g(char[5][10]); // identichno g(char(*)[10]); g(char[7][10]); // identichno g(char(*)[10]); g(char(*)[20]); // otlichno ot g(char(*)[10]); R.13.1 Sopostavlenie opisanij Dva opisaniya funkcij s odinakovymi imenami otnosyatsya k odnoj i toj zhe funkcii, esli oni nahodyatsya v odnoj oblasti vidimosti i imeyut identichnye tipy parametrov ($$R.13). Funkciya-chlen proizvodnogo klassa otnositsya k inoj oblasti vidimosti, chem funkciya-chlen bazovogo klassa s tem zhe imenem. Rassmotrim primer: class B { public: int f(int); }; class D : public B { public: int f(char*); }; Zdes' D::f(char*) skoree skryvaet B::f(int), chem peregruzhaet etu funkciyu. void h(D* pd) { pd->f(1); // oshibka: D::f(char*) skryvaet B::f(int) pd->B::f(1); // normal'no pd->f("Ben"); // normal'no, vyzov D::f } Funkciya, opisannaya lokal'no, nahoditsya v inoj oblasti vidimosti, chem funkciya s fajlovoj oblast'yu vidimosti. int f(char*); void g() { extern f(int); f("asdf"); // oshibka: f(int) skryvaet f(char*) poetomu // v tekushchej oblasti vidimosti net f(char*) } Dlya raznyh variantov peregruzhennoj funkcii-chlena mozhno zadat' raznye pravila dostupa, naprimer: class buffer { private: char* p; int size; protected: buffer(int s, char* store) { size = s; p = store; } // ... public: buffer(int s) { p = new char[size = s]; } }; R.13.2 Sopostavlenie parametrov Pri vyzove funkcii s dannym imenem proishodit vybor iz vseh funkcij s etim imenem, kotorye nahodyatsya v tekushchej oblasti vidimosti, i dlya kotoryh sushchestvuyut preobrazovaniya tipa, delayushchie vyzov vozmozhnym. Vybiraetsya ta funkciya, kotoraya naibolee sootvetstvuet fakticheskim parametram. Ona nahoditsya v oblasti peresecheniya mnozhestv funkcij, kazhdoe iz kotoryh naibolee sootvetstvuyut vyzovu po dannomu fakticheskomu parametru. Operaciya vyzova schitaetsya dopustimoj, esli v etom peresechenii nahoditsya tol'ko odin chlen. Funkciya, vybrannaya takim obrazom, dolzhna bolee lyuboj drugoj funkcii s tem zhe imenem sootvetstvovat' vyzovu, hotya by po odnomu iz parametrov (neobyazatel'no eto budet odin i tot zhe parametr dlya raznyh funkcij). V protivnom sluchae, vyzov schitaetsya nedopustimym. Pri sopostavlenii parametrov rassmatrivayut funkciyu s chislom standartnyh znachenij parametrov ($$R.8.2.6), ravnym n, kak n+1 funkcij s razlichnym chislom parametrov. Pri sopostavlenii parametrov nestaticheskuyu funkciyu-chlen rassmatrivayut kak funkciyu, imeyushchuyu dopolnitel'nyj parametr, ukazyvayushchij na ob容kt, dlya kotorogo vyzyvaetsya funkciya. |tot dopolnitel'nyj formal'nyj parametr dolzhen sopostavlyat'sya ili s ob容ktom, ili s ukazatelem na ob容kt, zadannymi v yavnoj operacii vyzova funkcii-chlena ($$R.5.2.4), ili zhe s pervym operandom peregruzhennoj funkcii operator ($$R.13.4). Dlya etogo dopolnitel'nogo parametra ne ispol'zuetsya nikakih vremennyh ob容ktov, a dlya dostizheniya sopostavleniya ne proizvoditsya nikakih pol'zovatel'skih preobrazovanij tipa. Esli yavno vyzyvaetsya chlen klassa X, ispol'zuya ukazatel' i operaciyu ->, to schitaetsya, chto dopolnitel'nyj parametr imeet tip const* X dlya chlenov tipa const, volatile* X dlya chlenov tipa volatile i X* dlya vseh ostal'nyh chlenov. Esli yavno vyzyvaetsya funkciya-chlen, ispol'zuya ob容kt i operaciyu ., a takzhe, esli vyzyvaetsya funkciya dlya pervogo operanda peregruzhennoj funkcii operator ($$R.9.4), to schitaetsya, chto dopolnitel'nyj parametr imeet tip: const X& dlya chlenov tipa const, volatile X& dlya chlenov tipa volatile i X& dlya vseh ostal'nyh chlenov. Pervyj operand dlya ->* i .* rassmatrivaetsya tak zhe, kak i pervyj operand dlya -> i . sootvetstvenno. |llipsis v spiske formal'nyh parametrov ($$R.8.2.5) mozhet sopostavlyat'sya s fakticheskim parametrom lyubogo tipa. Dlya dannogo fakticheskogo parametra dopuskaetsya tol'ko takaya posledovatel'nost' preobrazovanij tipa, kotoraya soderzhit ne bolee odnogo pol'zovatel'skogo preobrazovaniya. Ee nel'zya sokratit', isklyuchiv odno ili neskol'ko preobrazovanij, do posledovatel'nosti, kotoraya takzhe privodit k tipu, sopostavimomu s tipom rassmatrivaemogo formal'nogo parametra. Takaya posledovatel'nost' preobrazovanij nazyvaetsya naibolee sootvetstvuyushchej posledovatel'nost'yu. Naprimer, posledovatel'nost' int->float->double zadaet preobrazovanie int v double, no ee nel'zya nazvat' naibolee sootvetstvuyushchej posledovatel'nost'yu, poskol'ku v nej soderzhitsya bolee korotkaya posledovatel'nost' int->double. Krome opisannyh nizhe sluchaev, sleduyushchie trivial'nye preobrazovaniya tipa T ne vliyayut na svojstvo posledovatel'nosti byt' naibolee sootvetstvuyushchej: ishodnyj tip tip rezul'tata T T& T& T T[] T* T(parametry) T(*)(parametry) T const T T volatile T T* const T* T* volatile T* Posledovatel'nosti trivial'nyh preobrazovanij, kotorye otlichayutsya tol'ko poryadkom preobrazovanij, schitayutsya sovpadayushchimi. Otmetim, chto dlya funkcij s formal'nym parametrom tipa T, const T, volatile T, T&, const T& i volatile T& dopustim fakticheskij parametr iz odno i togo zhe mnozhestva znachenij. Pri neobhodimosti dlya razdeleniya posledovatel'nostej preobrazovanij ispol'zuyutsya specifikacii const i volatile, kak opisano v pravile [1] nizhe. Dlya formal'nogo parametra tipa T& trebuetsya vremennaya peremennaya v sluchayah, esli: fakticheskij parametr ne yavlyaetsya adresom, ili imeet tip, otlichnyj ot T, v tom chisle tip volatile. Nalichie takoj peremennoj ne vliyaet na sopostavlenie parametrov. Odnako, ono mozhet povliyat' na dopustimost' rezul'tata sopostavleniya, t.k. vremennuyu peremennuyu nel'zya ispol'zovat' dlya inicializacii ssylok, ne yavlyayushchihsya const ($$R.8.4.3). Posledovatel'nosti preobrazovanij rassmatrivayutsya soglasno sleduyushchim pravilam: [1] Tochnoe sopostavlenie. Posledovatel'nosti iz nulya ili bolee trivial'nyh preobrazovanij predpochtitel'nee lyubyh drugih posledovatel'nostej. Iz bolee slozhnyh posledovatel'nostej naibolee predpochtitel'ny te, v kotoryh net preobrazovanij T* v const T*, T* v volatile T*, T& v const T& ili T& v volatile T&. [2] Sopostavlenie so standartnymi preobrazovaniyami osnovnyh tipov. Iz posledovatel'nostej, ne otnosyashchihsya k [1], naibolee predpochtitel'ny te, kotorye soderzhat tol'ko standartnye celochislennye preobrazovaniya ($$R.4.1), preobrazovaniya float v double i trivial'nye preobrazovaniya. [3] Sopostavlenie s lyubymi standartnymi preobrazovaniyami. iz posledovatel'nostej, ne otnosyashchihsya k [2], naibolee predpochtitel'ny te, kotorye soderzhat tol'ko lyubye standartnye preobrazovaniya ($$R.4.1, $$R.4.2, $$R.4.3, $$R.4.4, $$R.4.5, $$R.4.6, $$R.4.7, $$R.4.8) i trivial'nye preobrazovaniya. Dlya etih posledovatel'nostej esli A yavlyaetsya pryamym ili kosvennym obshchim bazovym dlya klassa B, to preobrazovanie B* v A* predpochtitel'nee preobrazovaniya B* v void* ili const void*. Dalee, esli B yavlyaetsya pryamym ili kosvennym bazovym klassom dlya C, to predpochtitel'nee preobrazovanie C* v B*, chem C* v A*, i predpochtitel'nee preobrazovanie C& v B&, chem C& v A&. Ierarhiya klassov vystupaet zdes' kriterij otbora preobrazovanij ukazatelya v chlen ($$R.4.8). [4] Sopostavlenie s pol'zovatel'skimi preobrazovaniyami. Iz posledovatel'nostej, ne otnosyashchihsya k [3], naibolee predpochtitel'ny te, kotorye soderzhat tol'ko pol'zovatel'skie ($$R.12.3), standartnye ($$R.4) i trivial'nye preobrazovaniya. [5] Sopostavlenie s ellipsisom. Posledovatel'nosti, kotorye trebuyut sopostavleniya s ellipsisom, schitayutsya naimenee predpochtitel'nymi. Pol'zovatel'skie preobrazovaniya vybirayut, ishodya iz tipa peremennoj, kotoraya inicializiruetsya ili kotoroj prisvaivaetsya znachenie. class Y { // ... public: operator int(); operator double(); }; void f(Y y) { int i = y; // vyzov Y::operator int() double d; d = y; // vyzov Y::operator double() float f = y; // oshibka: neodnoznachnost' } Standartnye preobrazovaniya ($$R.4) mogut primenyat'sya k parametru, kak do pol'zovatel'skogo preobrazovaniya, tak i posle nego. struct S { S(long); operator int(); }; void f(long), f(char*); void g(S), g(char*); void h(const S&), h(char*); void k(S& a) { f(a); // f(long(a.operator int())) g(1); // g(S(long(1))) h(1); // h(S(long(1))) } Esli dlya parametra trebuetsya pol'zovatel'skoe preobrazovanie, to ne uchityvayutsya nikakie standartnye preobrazovaniya, kotorye mogut zatragivat' etot parametr, naprimer: class x { public: x(int); }; class y { public: y(long); }; void f(x); void f(y); void g() { f(1); // neodnoznachnost' } Zdes' vyzov f(1) neodnoznachen. Nesmotrya na to, chto dlya vyzova f(y(long(1))) trebuetsya na odno standartnoe preobrazovanie bol'she, chem dlya vyzova f(x(1)), vtoroj vyzov ne yavlyaetsya predpochtitel'nym. Preobrazovaniya s pomoshch'yu konstruktora ($$R.12.1) i s pomoshch'yu funkcii preobrazovaniya ($$R.12.3.2) ravnopravny. struct X { operator int(); }; struct Y { Y(X); }; Y operator+(Y,Y); void f(X a, X b) { a+b; // oshibka, neodnoznachnost': // operator+(Y(a), Y(b)) ili // a.operator int() + b.operator int() } R.13.3 Adres peregruzhennoj funkcii Kogda funkciya s nekotorym imenem ispol'zuetsya bez parametrov, sredi vseh funkcij s takim imenem v tekushchej oblasti vidimosti vybiraetsya edinstvennaya, kotoraya tochno sootvetstvuet naznacheniyu. Naznacheniem mozhet byt': inicializiruemyj ob容kt ($$R.8.4); levaya chast' operacii prisvaivaniya ($$R.5.17); formal'nyj parametr funkcii ($$R.5.2.2); formal'nyj parametr pol'zovatel'skoj operacii ($$R.13.4); tip znacheniya, vozvrashchaemogo funkciej ($$R.8.2.5). Otmetim, chto esli f() i g() yavlyayutsya peregruzhennymi funkciyami, to dlya pravil'noj interpretacii f(&g) ili ekvivalentnogo vyrazheniya f(g) nuzhno rassmotret' peresechenie mnozhestv vybora dlya f() i g(). Privedem primer: int f(double); int f(int); int (*pfd)(double) = &f; int (*pfi)(int) = &f; int (*pfe)(...) = &f; // oshibka: nesootvetstvie tipov Poslednyaya inicializaciya oshibochna, ne iz-za neodnoznachnosti, a potomu, chto ne opredeleno ni odnoj funkcii f() tipa int(...). Otmetim, chto ne sushchestvuet nikakogo standartnogo preobrazovaniya ($$R.4) ukazatelya na funkciyu odnogo tipa v ukazatel' na funkciyu drugogo tipa ($$R.4.6). V chastnosti, dazhe esli B yavlyaetsya obshchim bazovym klassom D, dve sleduyushchie inicializacii nedopustimy: D* f(); B* (*p1)() = &f; // oshibka void g(D*); void (*p2)(B*) = &g; // oshibka R.13.4 Peregruzhennye operacii Peregruzhat' mozhno bol'shinstvo operacij. imya-funkcii-operator: operator operaciya operaciya: odin iz new delete + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- , ->* -> () [] Dve poslednie operacii - eto vyzov funkcii ($$R.5.2.2) i indeksaciya ($$R.5.2.1). Mozhno peregruzhat' sleduyushchie (kak binarnye, tak i unarnye) operacii: + - * & Nel'zya peregruzhat' sleduyushchie operacii: . .* :: ?: sizeof a takzhe i special'nye simvoly preprocessora # i ## ($$R.16). Obychno funkcii, zadayushchie operacii (funkciya-operator) ne vyzyvayutsya yavno, k nim obrashchayutsya dlya vypolneniya operacij ($$R.13.4.1, $$R.13.4.2). Odnako, k nim mozhno obrashchat'sya yavno, naprimer: complex z = a.operator+(b); // complex z = a+b void* p = operator new(sizeof(int)*n); Operacii new i delete opisany v $$R.5.3.3 i $$R.5.3.4 i k nim ne otnosyatsya perechislyaemye nizhe pravila. Funkciya-operator mozhet byt' funkciej-chlenom ili imet' po krajnej mere odin parametr tipa klass ili ssylka na klass. Nel'zya izmenit' prioritet, poryadok vypolneniya ili chislo operandov operacii, no mozhno izmenit' predopredelennoe naznachenie takih operacij: =, unarnaya & i ,(zapyatoj), esli oni primenyayutsya k ob容ktu tipa klass. Za isklyucheniem funkcii operator=(), funkciya-operator nasleduetsya. Pravila dlya operator=() dany v $$R.12.8. |kvivalentnost' nekotoryh operacij nad osnovnymi tipami (naprimer, ++a ekvivalentno a+=1) mozhet ne sohranyat'sya dlya takih zhe operacij nad klassami. Dlya nekotoryh operacij trebuetsya, chtoby v sluchae ispol'zovaniya osnovnyh tipov operand byl adresom (naprimer, dlya +=). |to trebovanie mozhet byt' snyato, esli operaciya zadana nad klassami. Peregruzhennaya operaciya ne mozhet imet' standartnye znacheniya parametrov ($$R.8.2.6). Operacii, kotorye yavno ne ukazany v $$R.13.4.3-$$R.13.4.7, dejstvuyut kak obychnye unarnye ili binarnye operacii, podchinyayushchiesya pravilam, privedennym v $$R.13.4.1 ili $$R.13.4.2. R.13.4.1 Unarnye operacii Prefiksnuyu unarnuyu operaciyu mozhno zadat' s pomoshch'yu nestaticheskoj funkcii-chlena ($$R.9.3), bez parametrov ili s pomoshch'yu funkcii, ne yavlyayushchejsya chlenom, s odnim parametrom. Takim obrazom, dlya vsyakoj prefiksnoj unarnoj operacii @, vyrazhenie @x mozhet interpretirovat'sya kak x.operator@() ili kak operator@(x). Esli opisany funkcii-operatory oboih vidov, to kakaya iz nih budet ispol'zovat'sya pri vyzove, opredelyaetsya pravilami sopostavleniya parametrov ($$R.13.2). Postfiksnye unarnye operacii, takie kak ++ i -- , ob座asnyayutsya v $$R.13.4.7. R.13.4.2 Binarnye operacii Binarnuyu operaciyu mozhno zadat' s pomoshch'yu nestaticheskoj funkcii-chlena ($$R.9.3), imeyushchej odin parametr, ili s pomoshch'yu funkcii, ne yavlyayushchejsya chlenom, s dvumya parametrami. Takim obrazom, dlya vsyakoj binarnoj operacii @ vyrazhenie x@y mozhet interpretirovat'sya kak x.operator@(y) ili kak operator@(x,y). Esli opisany funkcii-operatory oboih vidov, to kakaya iz nih budet ispol'zovat'sya pri vyzove, opredelyaetsya pravilami sopostavleniya parametrov ($$R.13.2). R.13.4.3 Prisvaivaniya Funkciya prisvaivaniya operator=() dolzhna byt' nestaticheskoj funkciej-chlenom. Ona ne nasleduetsya ($$R.12.8). Bolee togo, esli pol'zovatel' ne opredelil dlya klassa X funkciyu operator=, to ispol'zuetsya standartnaya funkciya operator=, kotoraya opredelyaetsya kak prisvaivanie po chlenam dlya klassa X. X& X::operator=(const X&a