C szabvány++

Const korrektség

mi az a”const korrektség”?

jó dolog. Ez azt jelenti, hogy a const kulcsszót használjuk a const objektumok mutációjának megakadályozására.

például, ha egy f() függvényt szeretne létrehozni, amely elfogadta a std::string – ot, plusz meg akarja ígérni a hívóknaknem változtatja meg a hívó std::string – ját, amely átkerül a f() – ra, akkor megkaphatja a f() – ot, hogy megkapja a std::stringparamétert…

  • void f1(const std::string& s); // Pass hivatkozással-nak nek-const
  • void f2(const std::string* sptr); // Pass by pointer-to-const
  • void f3(std::string s); // Pass by value

a pass by reference-to-const és pass by pointer-to – const esetekben a hívóstd::string funkcióinak f() függvényen belüli megváltoztatására irányuló kísérleteket a fordító hibaként jelöli meg a függvényben fordítási idő. Ez az ellenőrzés teljes egészében fordítási időben történik: a const-nek nincs futási ideje vagy sebességköltsége. A pass by value esetben (f3()) a hívott függvény megkapja a hívó std::string példányát. Ez azt jelenti, hogy a f3() megváltoztathatja a helyi példányt, de a másolat megsemmisül, amikor a f3() visszatér. Különösen a f3()nem tudja megváltoztatni a hívó std::string objektumát.

ellenkező példaként tegyük fel, hogy egy g() függvényt akart létrehozni, amely elfogadta a std::string értéket, de tudatni szeretné a hívókkal, hogy a g() megváltoztathatja a hívó std::string objektumát. Ebben az esetben a g() megkaphatja astd::string paramétert…

  • void g1(std::string& s); // Pass hivatkozás-to-non-const
  • void g2(std::string* sptr); // Pass by pointer-to-non-const

a const hiánya ezekben a függvényekben azt mondja a fordítónak, hogy megengedett (de nem kötelező) megváltoztatni a hívó std::string objektumát. Így átadhatják std::string – jüket a f() függvények bármelyikének, de csak f3()(az, amelyik megkapja a paraméterét “érték szerint”) átadhatja std::string – ját g1() – nek vagy g2() – nek. Ha a f1()vagy f2() valamelyik g() függvényt kell hívnia, akkor a std::string objektum helyi másolatát át kell adni a g() függvénynek; a f1() vagy a f2() paraméter nem adható át közvetlenül egyik g() függvénynek sem. Pl.,

void g1(std::string& s);void f1(const std::string& s){ g1(s); // Compile-time Error since s is const std::string localCopy = s; g1(localCopy); // Okay since localCopy is not const}

természetesen a fenti esetben a g1() által végrehajtott változtatások a localCopy objektumon történnek, amely lokális a f1() – ra.különösen nem történik változás a const paraméterben, amelyet a f1()hivatkozással adtak át.

hogyan kapcsolódik a “const korrektség” a szokásos típusbiztonsághoz?

a paraméter const -sségének deklarálása csak a típusbiztonság egy másik formája.

ha úgy találja, hogy a szokásos típusú biztonság segít a rendszerek helyes javításában (különösen nagy rendszerekben), akkor aconst korrektség is segít.

a const korrektség előnye, hogy megakadályozza, hogy véletlenül módosítson valamit, amire nem számított. Végül néhány extra billentyűleütéssel kell díszítenie a kódját (a const kulcsszó), azzal a haszonnal, hogy elmondja a fordítónak és más programozóknak néhány további fontos szemantikai információt— olyan információkat, amelyeket a fordító a hibák megelőzésére használ, más programozók pedig dokumentációként használják.

fogalmilag el lehet képzelni, hogy a const std::stringpéldául más osztály, mint a szokásos std::string, mivel a constváltozat fogalmilag hiányzik a nemconst változatban elérhető különféle mutatív műveletek. Például fogalmilag elképzelheti, hogy a const std::string egyszerűen nincs hozzárendelési operátor+= vagy bármilyen más mutatív művelet.

meg kell próbálnom, hogy a dolgok “előbb” vagy “később”helyesek legyenek?

a nagyon, nagyon, nagyon elején.

vissza folt const helyesség eredményez hógolyó hatás: minden const hozzá “itt” igényel további négy kell adni “ott.”

Add const Korán és gyakran.

mit jelent a” const X* p”?

ez azt jelenti, hogy p egy X osztályú objektumra mutat, de a p nem használható a X objektum megváltoztatására (természetesen pis NULL).

olvassa el jobbról balra: “p egy mutató egy állandó X-re.”

például, ha a X osztálynak van const tagfüggvénye, például inspect() const, akkor rendben vanp->inspect(). De ha a X osztálynak van egy nemconst tagfüggvénye, az mutate(), akkor hiba, ha azt mondod p->mutate().

fontos, hogy ezt a hibát a fordító fordításkor elkapja-nem végeznek futásidejű teszteket. Ez azt jelenti, hogy a constnem lassítja le a programot, és nem követeli meg, hogy extra teszteseteket írjon a dolgok futásidejű ellenőrzéséhez-a fordító fordítási időben végzi a munkát.

mi a különbség a “const X * p”,” X* const p “és a”const X* const p” között?

olvassa el a mutató deklarációit jobbról balra.

  • const X* p azt jelenti, hogy ” p egy X – re mutat, azaz const“: a Xobjektum nem módosíthatóp – on keresztül.
  • X* const p azt jelenti: “p egy const mutató egyX-re, amely nem const“: magát a pmutatót nem változtathatja meg, de a X objektumot a psegítségével módosíthatja.A
  • const X* const p azt jelenti, hogy “ap egy const mutató egy X – re, azaz const – re”: nem változtathatja meg magát a p mutatót, sem a Xobjektumot a p segítségével.

és, ó igen, említettem már, hogy jobbról balra kell olvasnom a mutató deklarációit?

mit jelent a “const X& x”?

ez azt jelenti, hogy x alias egy X objektumot, de ezt a Xobjektumot nem lehet megváltoztatni a x segítségével.

olvassa el jobbról balra: “Ax hivatkozás egy X – re, ami const.”

például, ha a X osztálynak van const tagfüggvénye, például inspect() const, akkor rendben vanx.inspect(). De ha a X osztálynak van egy nemconst tagfüggvénye, az mutate(), akkor ez hiba, ha azt mondod x.mutate().

ez teljesen szimmetrikus a const-ra mutató mutatókkal, beleértve azt a tényt is, hogy a fordító elvégzi az összes ellenőrzést fordítási időben, ami azt jelenti, hogy a const nem lassítja le a programot, és nem követeli meg, hogy extra teszteseteket írjon a dolgok futásidejű ellenőrzéséhez.

mit jelent az “X const& x” és az “X const* p”?A

X const& x egyenértékű a const X& x – vel, a X const* xpedig aconst X* x – vel.

vannak, akik a const-on-the-right stílust részesítik előnyben, “konzisztens const” – nek hívják, vagy Simon Brand által kitalált kifejezést használva “East const.”Valójában az” East const “stílus konzisztensebb lehet, mint az alternatíva: a” East const ” stílus mindig a const – et helyezi jobbra attól, amit alkot, míg a másik stílus néha a const – et helyezi balra és néha jobbra (a const mutató deklarációkhoz és a const tagfüggvényekhez).

az “East const” stílusban a const helyi változót a jobb oldali consthatározza meg:int const a = 42;. Hasonlóképpen a static változót, amely const, static double const x = 3.14; – ként definiáljuk.Alapvetően minden const az általa létrehozott dolog jobb oldalán végződik, beleértve a const – et is, amelynek a jobb oldalon kell lennie: const mutató deklarációk és const tagfüggvény.

az “East const” stílus szintén kevésbé zavaró, ha típusalnevekkel használják: miért van itt a foo és a bar különböző típusai?

using X_ptr = X*;const X_ptr foo;const X* bar;

az “East const” stílus használata világosabbá teszi ezt:

using X_ptr = X*;X_ptr const foo;X* const foobar;X const* bar;

itt egyértelműbb, hogy a foo és a foobar azonos típusúak, a bar pedig más típusúak.

az “East const” stílus is jobban megfelel a mutató deklarációknak. Kontraszt a hagyományos stílus:

const X** foo;const X* const* bar;const X* const* const baz;

az “East const” stílus

X const** foo;X const* const* bar;X const* const* const baz;

ezen előnyök ellenére a const-on-the-right stílus még nem népszerű, így a régi kód általában a hagyományos stílus.

van értelme az “X& const x” – nek?

nem, ez ostobaság.

hogy megtudja, mit jelent a fenti nyilatkozat, olvassa el jobbról balra: “Ax egy consthivatkozás a X – re”. De ez redundáns-a hivatkozások mindig const, abban az értelemben, hogy soha nem állíthatja újra a hivatkozást, hogy egy másik objektumra utaljon. Soha. A constértékkel vagy anélkül.

más szavakkal, a “X& const x” funkcionálisan egyenértékű a “X& x ” – vel. Mivel aconst hozzáadása a & után semmit sem nyer, nem szabad hozzáadnia: összezavarja az embereket — a const arra készteti az embereket, hogy a X const, mintha azt mondta volna, hogy “const X& x“.

mi az a “const tag funkció”?

olyan tagfüggvény, amely megvizsgálja (nem pedig mutálja) az objektumát.

a const tagfüggvényt const utótag jelzi közvetlenül a tagfüggvény paraméterlistája után. A const utótaggal rendelkező tagfunkciókat “const tagfunkcióknak” vagy “ellenőröknek” nevezzük.”Aconst utótag nélküli tagfüggvényeket “nemconst tagfüggvényeknek” vagy “mutátoroknak” nevezzük.”

class Fred {public: void inspect() const; // This member promises NOT to change *this void mutate(); // This member function might change *this};void userCode(Fred& changeable, const Fred& unchangeable){ changeable.inspect(); // Okay: doesn't change a changeable object changeable.mutate(); // Okay: changes a changeable object unchangeable.inspect(); // Okay: doesn't change an unchangeable object unchangeable.mutate(); // ERROR: attempt to change unchangeable object}

a unchangeable.mutate() meghívására tett kísérlet hiba a fordításkor. Nincs futásidejű hely vagy speedpenalty a const számára, és nem kell teszteseteket írni a futásidejű ellenőrzéshez.

a záró const on inspect() tag függvényt kell használni arra, hogy a módszer ne változtassa meg az objektum abstract (kliens-látható) állapotát. Ez kissé eltér attól, hogy a módszer nem változtatja meg az objektum struct” nyers bitjeit”. A C++ fordítóprogramok csak akkor vehetik igénybe a “bitenkénti” értelmezést, ha meg tudják oldani az aliasing problémát, amelyet általában nem lehet megoldani (azaz létezhet egy nemconst alias, amely módosíthatja az objektum állapotát). Egy másik (fontos) betekintés ebből az aliasing kérdésből: egy objektumra mutató mutató-to-constnem garantálja, hogy az objektum nem változik; csupán azt ígéri, hogy az objektum nem változik ezen a mutatón keresztül.

mi a kapcsolat a return-by-reference és a const tagfüggvény között?

ha a this objektum egy tagját egy inspector metódus hivatkozásával szeretné visszaadni, akkor azt a reference-to-const (const X& inspect() const) vagy value (X inspect() const) használatával kell visszaadnia.

class Person {public: const std::string& name_good() const; // Right: the caller can't change the Person's name std::string& name_evil() const; // Wrong: the caller can change the Person's name int age() const; // Also right: the caller can't change the Person's age // ...};void myCode(const Person& p) // myCode() promises not to change the Person object...{ p.name_evil() = "Igor"; // But myCode() changed it anyway!!}

a jó hír az, hogy a fordító gyakran elkapni, ha ezt rosszul. Különösen, ha véletlenül visszaadja a this objektum egy tagját nemconst hivatkozással, mint például a fenti Person::name_evil()-ben, a fordító gyakran észleli azt, és fordítási idejű hibát ad, amikor lefordítja a belsőségeket, ebben az esetbenPerson::name_evil().

a rossz hír az, hogy a fordító nem mindig elkapni: vannak olyan esetek, amikor a fordító egyszerűen nem evergive egy fordítási idő hibaüzenet.

Fordítás: meg kell gondolni. Ha ez megijeszt, keressen egy másik munkát; a “gondolkodás” nem négybetűs szó.

ne feledje, hogy a” const filozófia ” elterjedt ebben a szakaszban: a const tagfüggvénynek nem szabad megváltoztatnia (vagy lehetővé tennie a hívó fél számára, hogy megváltoztassa) a this objektum logikai állapotát (más néven absztrakt állapot, más néven jelentéswisestate). Gondolj arra, hogy mit jelent egy objektum, nem pedig arra, hogy hogyan valósítják meg belsőleg. Egy személy életkora és neve logikailagrésze a személynek, de a személy szomszédja és munkáltatója nem. Az inspector metódus, amely a thisobjektum logikai / absztrakt / jelentéstani állapotának egy részét adja vissza, nem adhat vissza nemconst mutatót (vagy hivatkozást) arra a részre,függetlenül attól, hogy az adott rész belsőleg közvetlen adattagként van-e megvalósítva fizikailag beágyazva athis objektumba, vagy más módon.

mi a helyzet a “const-túlterheléssel”?

const a túlterhelés segít a const helyesség elérésében.

const a túlterhelés akkor jelentkezik, ha egy inspector metódus és egy mutátor metódus azonos névvel és azonos számú és típusú paraméterrel rendelkezik. A két különböző módszer csak abban különbözik egymástól, hogy a felügyelő const, a mutátor pedig nemconst.

a const túlterhelés leggyakoribb használata az index operátor. Általában meg kell próbálnia használni az egyiketstandard konténer sablonok, például std::vector, de ha saját osztályt kell létrehoznia, amelynek van egy subscriptoperator, itt van a hüvelykujjszabály: az index operátorok gyakran párban jönnek.

class Fred { /*...*/ };class MyFredList {public: const Fred& operator (unsigned index) const; // Subscript operators often come in pairs Fred& operator (unsigned index); // Subscript operators often come in pairs // ...};

a constindex operátor visszaad egy const-hivatkozást, így a fordító megakadályozza a hívókat a Fred véletlen mutálásában/megváltoztatásában. A nem-const index operátor visszaad egy nem – const hivatkozást, amely gyakran azt mondja a hívóknak (és a fordítónak), hogy a hívók módosíthatják a Fred objektumot.

amikor a MyFredListosztályod felhasználója felhívja az index operátort, a fordító kiválasztja, hogy melyik túlterhelést hívja a MyFredList állandósága alapján. Ha a hívónak MyFredList a vagy MyFredList& a van, akkor a a felhívja a nemconst index operátort, a hívó pedig nemconst hivatkozást kap a Fred:

tegyük fel például, hogy class Fred rendelkezik egy inspector-metódussal inspect() const és egy mutor-metódussal mutate():

void f(MyFredList& a) // The MyFredList is non-const{ // Okay to call methods that inspect (look but not mutate/change) the Fred at a: Fred x = a; // Doesn't change to the Fred at a: merely makes a copy of that Fred a.inspect(); // Doesn't change to the Fred at a: inspect() const is an inspector-method // Okay to call methods that DO change the Fred at a: Fred y; a = y; // Changes the Fred at a a.mutate(); // Changes the Fred at a: mutate() is a mutator-method}

Ha azonban a hívónak const MyFredList a vagy const MyFredList& a van, akkor a a felhívja a const subcriptoperator-t, és a hívó végül consthivatkozást kap a Fred – re. Ez lehetővé teszi a hívó számára, hogy megvizsgálja a Fredértéket a – nél, de megakadályozza, hogy a hívó akaratlanul mutálja/megváltoztassa a Fred értéket a – nél.

void f(const MyFredList& a) // The MyFredList is const{ // Okay to call methods that DON'T change the Fred at a: Fred x = a; a.inspect(); // Compile-time error (fortunately!) if you try to mutate/change the Fred at a: Fred y; a = y; // Fortunately(!) the compiler catches this error at compile-time a.mutate(); // Fortunately(!) the compiler catches this error at compile-time}

az Index – és funcall-operátorok Const túlterhelését itt,itt, itt, itt és itt szemléltetjük.

természetesen használhatja a const -túlterhelést az index operátoron kívüli dolgokra is.

hogyan segíthet nekem jobb osztályok kialakításában, ha megkülönböztetem a logikai állapotot a fizikai állapottól?

mert ez arra ösztönöz, hogy az osztályokat kívülről tervezze meg-nem pedig belülről-kifelé, ami viszont megkönnyíti az osztályok és tárgyak megértését és használatát, intuitívabbá, kevésbé hibára hajlamos és gyorsabb. (Oké, ez egy kicsit túl egyszerűsítés. Ahhoz, hogy megértsük az összes if-et, csak el kell olvasnod a többi részetválasz!)

értsük meg ezt belülről-kívülről — meg kell terveznie az osztályokat a kívülről-be, de ha új vagy ebben a koncepcióban, akkor könnyebb megérteni a kívülről-kifelé.

belül a tárgyak fizikai (vagy konkrét vagy bitenkénti) állapotban vannak. Ez az az állapot, amelyet a programozók könnyen láthatnak és megérthetnek; ez az állapot lenne ott, ha az osztály csak egy C-stílusú structlenne.

kívülről az objektumaidnak az osztályod felhasználói vannak, és ezek a felhasználók csak publictagfunkciókat és friend s-t használhatnak. Ezek a külső felhasználók az objektumot állapotként is érzékelik, például ha az objektum osztály Rectangle módszerekkel width(), height() és area(), a felhasználók azt mondanák, hogy ez a három az objektum logikai (vagy absztrakt vagy jelentéstani) állapotának része. Egy külső felhasználó számára a Rectangle objektumnak van területe, még akkor is, ha azt menet közben számítják ki (pl. ha a area() metódus az objektum szélességének és magasságának szorzatát adja vissza). Valójában, és ez a fontos pont, a felhasználók nem tudják, és nem érdekli, hogyan hajtsák végre ezeket a módszereket; a felhasználók még mindig érzékelik, az ő szemszögükből, hogy az objektum logikailag ugyanolyan széles, magas és terület állapotú.

a area() példa olyan esetet mutat be, amikor a logikai állapot olyan elemeket tartalmazhat, amelyek nem valósulnak meg közvetlenül a fizikai állapotban. Az ellenkezője is igaz: az osztályok néha szándékosan elrejtik objektumaik fizikai(konkrét, bitenkénti) állapotának egy részét a felhasználók elől — szándékosan nem biztosítanak semmilyen public tagfunkciót vagyfriends-t, amely lehetővé tenné a felhasználók számára, hogy olvassanak vagy írjanak, vagy akár tudjanak erről a rejtett állapotról. Ez azt jelenti, hogy az objektum fizikai állapotában vannak olyan bitek, amelyeknek nincsenek megfelelő elemei az objektum logikai állapotában.

ez utóbbi eset példájaként egy gyűjtemény-objektum gyorsítótárazhatja utolsó keresését abban a reményben, hogy javítja a következő keresés teljesítményét. Ez a gyorsítótár minden bizonnyal része az objektum fizikai állapotának, de ott van egy belső végrehajtási részlet, amely valószínűleg nem lesz kitéve a felhasználóknak — valószínűleg nem lesz része az objektum logikai állapotának. Mondani, mi az, ami könnyű, ha úgy gondolja, kívülről-a: ha a gyűjtemény-objektum felhasználóinak most van módjuk ellenőrizni a gyorsítótár állapotát, akkor a gyorsítótár átlátszó, és nem része az objektum logikai állapotának.

a nyilvános tagfüggvényeim állandóságának azon kell alapulnia, hogy a módszer mit tesz az objektum logikai állapotával vagy fizikai állapotával?

logikus.

nincs mód arra, hogy ezt a következő részt megkönnyítsük. Fájni fog. A legjobb ajánlás az, hogy üljön le. És kérlek, a biztonságod érdekében, győződj meg róla, hogy nincsenek éles eszközök a közelben.

térjünk vissza a collection-object példához. Ne feledje: van egy keresési módszer, amelyeléri az utolsó keresést a jövőbeni keresések felgyorsítása érdekében.

tegyük fel, ami valószínűleg nyilvánvaló: tegyük fel, hogy a keresési módszer nem módosítja a gyűjtemény-objektum logikai állapotát.

tehát… eljött az idő, hogy bántsalak. Készen van?

itt jön: ha a keresési módszer nem változtatja meg a gyűjtemény-objektum logikai állapotát, de megváltoztatja a gyűjtemény-objektum fizikai állapotát (ez nagyon valós változást eredményez a nagyon valós gyorsítótárban), akkor a keresési metódusnak const – nek kell lennie?

a válasz hangos igen. (Minden szabály alól vannak kivételek,tehát az “Igen” mellett valóban csillagnak kell lennie, de az esetek túlnyomó többségében a válasz igen.)

ez az egész a “logikai const” felett “fizikai const.”Ez azt jelenti, hogy a döntést arról, hogy díszíteni amethod const kell függenie elsősorban attól, hogy ez a módszer hagyja a logikai állapot változatlan, függetlenül attól, hogy (ülsz?) (érdemes leülni) függetlenül attól, hogy a módszer nagyon valós változásokat hoz-e az objektum nagyon valós fizikai állapotában.

abban az esetben, ha ez nem süllyedt be, vagy ha még nem fáj, akkor két esetre osztjuk szét:

  • ha egy metódus megváltoztatja az objektum logikai állapotának bármely részét, akkor logikusan mutátor; nem lehet const evenif (ahogy valójában történik!) a módszer nem változtatja meg az objektum konkrét állapotának fizikai bitjeit.
  • ezzel szemben egy metódus logikailag inspector, és const – nek kell lennie, ha soha nem változtatja meg az objektum logikai állapotát, még akkor sem, ha (ahogy valójában történik!) a módszer megváltoztatja az objektum konkrét állapotának fizikai bitjeit.

ha összezavarodtál, olvasd el újra.

ha nem vagy összezavarodva, de dühös vagy, jó: lehet, hogy még nem tetszik, de legalább megérted. Vegyünk egy mély lélegzet, és ismételje meg utánam: “a constness módszer kell értelme kívülről a tárgy.”

ha még mindig dühös vagy, ismételd meg háromszor: “a metódus állandóságának értelmesnek kell lennie az objektum felhasználói számára, és ezek a felhasználók csak az objektum logikai állapotát láthatják.”

ha még mindig dühös vagy, sajnálom, ez az, ami. Szívd fel és élj vele együtt. Igen, lesznek kivételek; minden szabálynak megvan. De általában ez a logikai const fogalom jó neked és jó a szoftverednek.

még egy dolog. Ez őrült lesz, de legyünk pontosak abban, hogy egy módszer megváltoztatja-e az objektum logikai állapotát. Ha kívül esik az osztályon — normál felhasználó vagy, akkor minden kísérlet, amelyet elvégezhet (minden hívott módszer vagy módszersorozat), ugyanazokkal az eredményekkel jár (ugyanazok a visszatérési értékek, ugyanazok a kivételek vagy kivételek hiánya), függetlenül attól, hogy először hívta-e meg ezt a keresési módszert. Ha a keresési függvény megváltoztatta bármely jövőbeli viselkedését (nem csak gyorsabbá tette, hanem megváltoztatta az eredményt, megváltoztatta a visszatérési értéket, megváltoztatta az exception — t), akkor a keresési módszer megváltoztatta az objektum logikai állapotát-ez egy kölcsönző. De ha a keresési módszer nem változott mást, mint talán néhány dolgot gyorsabbá tenni, akkor ez egy ellenőr.

mit tegyek, ha azt akarom, hogy egy const tag függvény “láthatatlan” változást hajtson végre egy adat tagon?

használja a mutable(vagy végső megoldásként használja a const_cast lehetőséget).

az ellenőrök kis százalékának módosítania kell egy objektum fizikai állapotát, amelyet a külső felhasználók nem tudnak megfigyelni — a fizikai, de nem logikai állapot változásai.

például a korábban tárgyalt gyűjteményobjektum gyorsítótárazta az utolsó keresést, remélve, hogy javítja a következő keresés teljesítményét. Mivel a gyorsítótárat ebben a példában a gyűjtemény-objektum nyilvános felületének egyetlen része sem tudja közvetlenül megfigyelni (az időzítésen kívül), létezése és állapota nem része az objektum logikai állapotának, így a változások láthatatlanok a külső felhasználók számára. A keresési módszer ellenőr, mivel soha nem változtatja meg az objektum logikai állapotát, függetlenül attól, hogy legalább a jelenlegi megvalósítás szempontjából megváltoztatja az objektum fizikai állapotát.

amikor a módszerek megváltoztatják a fizikai, de nem logikai állapotot, a módszert általában const-ként kell megjelölni, mivel valójában egy inspector-módszer. Ez problémát okoz: amikor a fordító meglátja a const metódust, amely megváltoztatja a this objektum fizikai állapotát, panaszkodni fog — hibaüzenetet ad a kódjának.

a C++ fordító nyelve a mutable kulcsszót használja, hogy segítsen megragadni ezt a logikai const fogalmat. Ebben az esetben a gyorsítótárat a mutable kulcsszóval jelöljük meg, így a fordító tudja, hogy megengedett aconst metóduson belül vagy bármely más const mutatón vagy hivatkozáson keresztül. Nyelvünkben a mutable kulcsszó jelöli az objektum fizikai állapotának azokat a részeit, amelyek nem részei a logikai állapotnak.

a mutablekulcsszó közvetlenül az adattag nyilatkozata előtt megy, vagyis ugyanazon a helyen, ahol aconst – et elhelyezheti. A másik, nem preferált megközelítés a const‘a this mutató értéke, valószínűleg aconst_cast kulcsszó:

Set* self = const_cast<Set*>(this); // See the NOTE below before doing this!

e sor után a self ugyanazokkal a bitekkel fog rendelkezni, mint a this, Vagyis self == this, de a self inkább Set*, mintconst Set* (technikailag a this const Set* const, de a jobb oldali const lényegtelen ebben a vitában).Ez azt jelenti, hogy a self használatával módosíthatja a thisáltal mutatott objektumot.

megjegyzés: rendkívül valószínűtlen hiba fordulhat elő const_cast esetén. Csak akkor fordul elő, ha három nagyon ritka dolgot kombinálnak egyszerre: olyan adattag, amelynek mutable-nek kell lennie (mint a fentiekben tárgyaltuk), egy fordító, amely nem támogatja a mutable kulcsszót és/vagy egy programozó, aki nem használja, és egy objektum, amelyet eredetileg const-nek definiáltak (szemben egy normál, nemconst objektummal, amelyre egy mutató mutat-to – const).Bár ez a kombináció olyan ritka, hogy soha nem történhet meg veled, ha valaha is megtörtént, a kód nem működik (a szabvány szerint a viselkedés nincs meghatározva).

ha valaha is használni szeretné a const_cast – ot, akkor használja a mutable – et. Más szóval, ha valaha is meg kell változtatnod az anobject egy tagját, és az objektumra mutat egy mutató-to-const, a legbiztonságosabb és legegyszerűbb dolog, ha hozzáadod a mutable értéket a tag nyilatkozatához. Használhatja a const_cast – ot, ha biztos benne, hogy a tényleges objektum nem const (pl. ha biztos benne, hogy az objektum valami ilyesmit deklarál: Set s;), de ha maga az objektum lehet const (pl. ifit deklarálható: const Set s;), akkor használja a mutable – et a const_casthelyett.

kérjük, ne írjon mondván version X fordító Y gép Z lehetővé teszi, hogy módosítsa a nemmutable tagja egyconst objektumot. Nem érdekel — a nyelv szerint illegális, és a kód valószínűleg sikertelen lesz egy másik fordítóprogramon, vagy akár ugyanazon fordító más verzióján (frissítésén). Csak mondj nemet. Használja helyette a mutable értéket. Írj kódamely garantáltan működik, nem olyan kód, amely úgy tűnik, hogy nem törik meg.

a const_cast Elveszett optimalizálási lehetőségeket jelent?

elméletben igen; a gyakorlatban nem.

még akkor is, ha a nyelv törvényen kívül helyezte const_cast, az egyetlen módja annak, hogy elkerülje a regiszter gyorsítótárának átöblítését egy const tagfunkció hívás esetén, az aliasing probléma megoldása (azaz., annak bizonyítására, hogy nincsenek nemconst mutatók, amelyek az objektumra mutatnak). Ez csak ritka esetekben fordulhat elő (amikor az objektum a const tagfüggvény-meghívás hatókörében van felépítve, és amikor az objektum felépítése és aconst tagfüggvény-meghívás közötti összes nemconst tagfüggvény-meghívás statikusan kötött, és amikor ezen meghívások mindegyike szintén inlined, és amikor maga a konstruktor inlined, és amikor bármely tagfüggvény a konstruktor hívásai inline).

miért engedi meg a fordító, hogy megváltoztassak egy int-t, miután egy const int* – vel mutattam rá?

mivel a “const int* p” azt jelenti, hogy “p megígéri, hogy nem változtatja meg a *p,” nem “*p megígéri, hogy nem változik.”

ha a const int* egy int-re mutat, az nem const – ify a int. A int nem változtatható meg aconst int* – en keresztül, de ha valaki másnak van egy int* (megjegyzés: nincs const), amely ugyanarra a int – re mutat (“álnevek”), akkor ez aint*használható a int megváltoztatására. Például:

void f(const int* p1, int* p2){ int i = *p1; // Get the (original) value of *p1 *p2 = 7; // If p1 == p2, this will also change *p1 int j = *p1; // Get the (possibly new) value of *p1 if (i != j) { std::cout << "*p1 changed, but it didn't change via pointer p1!\n"; assert(p1 == p2); // This is the only way *p1 could be different }}int main(){ int x = 5; f(&x, &x); // This is perfectly legal (and even moral!) // ...}

vegye figyelembe, hogy a main() és f(const int*,int*) különböző összeállítási egységekben lehetnek, amelyek a hét különböző napjain vannak összeállítva. Ebben az esetben a fordító nem tudja észlelni az álnevet fordításkor. Ezért nincs mód arra, hogy olyan nyelvi szabályt alkossunk, amely tiltja az ilyesmit. Valójában nem is szeretnénk ilyen szabályt készíteni, mivel általában olyan tulajdonságnak tekintik, hogy sok mutató mutathat ugyanarra a dologra. Az a tény, hogy az egyik mutató azt ígéri, hogy nem változtatja meg az alapul szolgáló “dolgot”, csak a mutató ígérete; ez nem a “dolog”ígérete.

a “const Fred* p” azt jelenti, hogy *p nem változhat?

nem! (Ez a int mutatók álnévvel kapcsolatos GYIK-hez kapcsolódik.)

const Fred* p” azt jelenti, hogy a Fred nem változtatható meg a mutatóval p, de lehet, hogy más módon is eljuthat az objektumhoz anélkül, hogy átmenne a const (például egy álneves nemconst mutató, például a Fred*). Például, ha két “const Fred* p” és “Fred* q” mutató van, amelyek ugyanarra a Fred objektumra mutatnak (álnév), a q mutató használható a Fred objektum megváltoztatására, de a p mutató nem.

class Fred {public: void inspect() const; // A const member function void mutate(); // A non-const member function};int main(){ Fred f; const Fred* p = &f; Fred* q = &f; p->inspect(); // Okay: No change to *p p->mutate(); // Error: Can't change *p via p q->inspect(); // Okay: q is allowed to inspect the object q->mutate(); // Okay: q is allowed to mutate the object f.inspect(); // Okay: f is allowed to inspect the object f.mutate(); // Okay: f is allowed to mutate the object // ...}

Miért kapok hibát egy Foo** const foo * * konvertálásakor?

mert a Foo** const Foo** konvertálása érvénytelen és veszélyes lenne.

a C++ lehetővé teszi a (biztonságos) konverziót Foo* Foo const*, de hibát ad, ha megpróbálja implicit módon konvertálni Foo**const Foo**.

az alábbiakban bemutatjuk, miért jó ez a hiba. De először itt van a leggyakoribb megoldás: egyszerűenváltoztassa meg a const Foo** – et const Foo* const* – ra:

class Foo { /* ... */ };void f(const Foo** p);void g(const Foo* const* p);int main(){ Foo** p = /*...*/; // ... f(p); // ERROR: it's illegal and immoral to convert Foo** to const Foo** g(p); // Okay: it's legal and moral to convert Foo** to const Foo* const* // ...}

a Foo** const Foo** – ről való átalakítás azért veszélyes, mert lehetővé teszi, hogy csendben és véletlenül módosíts egy const Foo tárgyat öntvény nélkül:

class Foo {public: void modify(); // make some modification to the this object};int main(){ const Foo x; Foo* p; const Foo** q = &p; // q now points to p; this is (fortunately!) an error *q = &x; // p now points to x p->modify(); // Ouch: modifies a const Foo!! // ...}

ha a q = &p vonal legális lenne, a qa p – ra mutatna. A következő sor, *q = &x, maga a p(mivel a *q p) a x pontra mutat. Ez rossz dolog lenne, mivel elvesztettük volna a const minősítőt: a p egyFoo*, de a x egy const Foo. A p->modify()vonal kihasználja pazon képességét, hogy módosítsa a referensét,ami az igazi probléma, mivel végül módosítottuk a const Foo.

analógia útján, ha egy bűnözőt törvényes álruhában rejtenek el, akkor kihasználhatja az álruhába vetett bizalmat.Az rossz.

szerencsére a C++ megakadályozza ezt: a q = &p sort a C++ fordító fordítási idejű hibaként jelöli meg. Emlékeztető: kérjük, ne pointer-öntött az utat körül, hogy fordítási idejű hibaüzenet. Csak Mondj Nemet!

(megjegyzés: van egy fogalmi hasonlóság E között és a Derived**Base** – ra való áttérés tilalma között.)

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.