Standaard C++

Const correctheid

Wat is “const correctheid”?

een goede zaak. Het betekent het gebruik van het trefwoord const om te voorkomen dat const objecten worden gemuteerd.

bijvoorbeeld, als je wilde om een functie f() dat aanvaard std::string, plus u wilt belofte callersnot wijzigen van het nummer van de beller std::string dat wordt doorgegeven aan f() u kunt f() ontvangen std::stringparameter…

  • void f1(const std::string& s); // langs referentie–const
  • void f2(const std::string* sptr); // Pas door de aanwijzer naar-const
  • void f3(std::string s); // Pass waarde

In het doorgeven door verwijzing-naar-const en langs een pointer-naar-const gevallen, alle pogingen tot het wijzigen van de bellerstd::string in de f() functies zouden worden gemarkeerd door de compiler een fout op compileertijd. Deze controle wordt volledig uitgevoerd tijdens het compileren: er zijn geen run-time ruimte of snelheid kosten voor de const. In het geval pass by value (f3()) krijgt de aangeroepen functie een kopie van std::stringvan de beller. Dit betekent dat f3() zijn localcopy kan wijzigen, maar de kopie wordt vernietigd wanneer f3() terugkeert. In het bijzonder kan f3() het std::stringobject van de beller niet wijzigen.

stel dat u een functie g() wilt aanmaken die een std::string accepteert, maar dat u callers wilt laten weten dat g() het std::string object van de beller kan veranderen. In dit geval kunt u g() de parameterstd::string laten ontvangen…

  • void g1(std::string& s); // passeer door verwijzing-naar-non-const
  • void g2(std::string* sptr); // passeer pointer-to-non-const

het ontbreken van const in deze functies vertelt de compiler dat ze het std::string object van thecaller mogen (maar niet verplicht zijn) wijzigen. Zo kunnen ze hun std::string doorgeven aan een van de f() functies, maar alleen f3()(degene die de parameter “op waarde” ontvangt) kan zijn std::string doorgeven aan g1() of g2(). Als f1() of f2()een g() functie moet aanroepen, moet een lokale kopie van het std::string object worden doorgegeven aan de g() functie; deparameter aan f1() of f2() kan niet direct worden doorgegeven aan een van beide g() functies. Bijv..,

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}

natuurlijk worden in het bovenstaande geval alle wijzigingen die g1() aanbrengt aangebracht in het localCopy object dat lokaal is tot f1().in het bijzonder zullen geen wijzigingen worden aangebracht in de const parameter die werd doorgegeven door verwijzing naar f1().

hoe verhoudt “const correctheid” zich tot gewone veiligheid?

declareren van de const-heid van een parameter is gewoon een andere vorm van typeveiligheid.

als u vindt dat gewone type veiligheid U helpt om systemen correct te krijgen (dat doet het; vooral in grote systemen), zult u ookconst correctheid helpen.

het voordeel van const correctheid is dat het voorkomt dat u per ongeluk iets wijzigt waarvan u niet verwachtte dat het zou worden gewijzigd. Uiteindelijk moet je je code versieren met een paar extra toetsaanslagen (het const sleutelwoord), met het voordeel dat je de compiler en andere programmeurs een extra stuk belangrijke semantische informatie vertelt— informatie die de compiler gebruikt om fouten te voorkomen en andere programmeurs gebruiken als documentatie.

conceptueel kunt u zich voorstellen dat const std::string bijvoorbeeld een andere klasse is dan gewone std::string, omdat de const variant conceptueel de verschillende mutatieve operaties mist die beschikbaar zijn in de niet-constvariant. U kunt zich bijvoorbeeld conceptueel voorstellen dat een const std::string eenvoudigweg geen toewijzings-operator+= of andere mutatieve operaties heeft.

moet ik proberen om dingen correct te krijgen “vroeger” of “later”?

aan het zeer, zeer, zeer begin.

Back-patching const correctheid resulteert in een sneeuwbaleffect: elke const die u toevoegt “hier” vereist vier moreto worden toegevoegd “daar.”

voeg const vroeg en vaak toe.

wat betekent “const X * p”?

het betekent p wijst naar een object van klasse X, maar p kan niet worden gebruikt om dat X object te veranderen (natuurlijk kan p ook NULLzijn).

lees het van rechts naar links: “p is een aanwijzer naar een X die constant is.”

bijvoorbeeld, als klasse X een const member functie heeft zoals inspect() const, is het goed omp->inspect()te zeggen. Maar als klasse X een niet – const member functie heeft genaamd mutate(), is het een fout als je p->mutate()zegt.

significant, deze fout wordt opgevangen door de compiler tijdens het compileren-er worden geen run — time tests gedaan. Dat betekent dat constuw programma niet vertraagt en dat u geen extra testcases hoeft te schrijven om dingen tijdens runtime te controleren-de compiler doet het werk tijdens het compileren.

Wat is het verschil tussen “const X* p”, “X* const p” en “const X* const p”?

lees de verwijzingen van rechts naar links.

  • const X* p betekent “p wijst naar een X die const is”: het X object kan niet worden gewijzigd viap.
  • X* const p betekent “p is een const pointer naar een X die niet – constis”: u kunt de aanwijzer pzelf niet wijzigen, maar u kunt het X object wijzigen via p.
  • const X* const p betekent “p is een const pointer naar een X die const is”: U kunt de pointer pzelf niet wijzigen, noch kunt u het X object wijzigen via p.

en, oh ja, had ik al gezegd dat ik uw verwijzingen van rechts naar links moest lezen?

wat betekent “const X& x”?

het betekent x aliassen een X object, maar u kunt dat X object niet wijzigen via x.

lees het van rechts naar links: “x is een verwijzing naar een X die constis.”

bijvoorbeeld, als klasse X een const member functie heeft zoals inspect() const, is het goed omx.inspect()te zeggen. Maar als klasse X een niet-const member functie genaamd mutate() heeft, is het een errorif als je x.mutate()zegt.

dit is geheel symmetrisch met verwijzingen naar const, inclusief het feit dat de compiler alle controles uitvoert tijdens het compileren, wat betekent dat const uw programma niet vertraagt en dat u geen extra testcases hoeft te schrijven om dingen tijdens runtime te controleren.

Wat betekenen” X const& x” en “X const* p”?

X const& x is gelijk aan const X& x, en X const* x is gelijk aanconst X* x.

sommige mensen geven de voorkeur aan de const-op-de-juiste stijl, Ze noemen het “consistent const” of, gebruikmakend van een term bedacht door Simon Brand, “East const.”De” East const ” stijl kan inderdaad consistenter zijn dan het alternatief: de “East const” stijl geeft altijd de const aan de rechterkant van wat het bevat, terwijl de andere stijl soms de const aan de linkerkant en soms aan de rechterkant plaatst (voor const pointer declaraties en const member functies).

met de” East const ” stijl, een lokale variabele die const is wordt gedefinieerd met de const aan de rechterkant:int const a = 42;. Een variabele static die const is, wordt eveneens gedefinieerd als static double const x = 3.14;.In principe eindigt elke const aan de rechterkant van het ding dat het bevat, inclusief de const die rechts moet staan: const pointer declaraties en met een const member functie.

de “East const” – stijl is ook minder verwarrend bij gebruik met type aliassen: Waarom hebben foo en bar hier verschillende typen?

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

het gebruik van de” East const ” stijl maakt dit duidelijker:

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

het is hier duidelijker dat foo en foobar hetzelfde type zijn en dat bar een ander type is.

de “East const” stijl is ook meer consistent met pointer declaraties. Contrast van de traditionele stijl:

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

met de “East const” stijl

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

ondanks deze voordelen is de const-op-de-juiste stijl nog niet populair, dus oudere code heeft de neiging om de traditionele stijl te hebben.

slaat “X& const x” ergens op?

Nee, Het is onzin.

om erachter te komen wat de bovenstaande verklaring betekent, Lees deze van rechts naar links: “x is een constreferentie naar een X“. Maar dat is redundant — referenties zijn altijd const, in die zin dat je nooit een referentie opnieuw kunt plaatsen om het naar een ander object te laten verwijzen. Nooit. Met of zonder const.

met andere woorden, “X& const x” is functioneel gelijk aan “X& x“. Omdat u niets wint door deconst na de & toe te voegen, moet u deze niet toevoegen: het zal mensen in verwarring brengen — de const zal sommige mensen doen denken dat de X const is, alsof u “const X& x“had gezegd.

Wat is een “const member function”?

een lidfunctie die zijn object inspecteert (in plaats van muteert).

een const member-functie wordt aangegeven door een const achtervoegsel net na de parameterlijst van de member-functie. Ledenfuncties met een const achtervoegsel worden “const ledenfuncties” of “inspecteurs” genoemd.”Member-functies zonderconst achtervoegsel worden “non-const member-functies” of “mutators” genoemd.”

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}

de poging om unchangeable.mutate() aan te roepen is een fout tijdens het compileren. Er is geen runtime-ruimte of speedpenalty voor const, en u hoeft geen testcases te schrijven om het tijdens runtime te controleren.

de trailing const op inspect() member functie moet worden gebruikt om te betekenen dat de methode de status van het object ‘ abstract (client-visible) niet verandert. Dat is iets anders dan zeggen dat de methode de “ruwe bits” van structvan theobject niet zal veranderen. C++ compilers zijn niet toegestaan om de “bitwise” interpretatie te nemen, tenzij ze het liasing probleem kunnen oplossen, wat normaal niet kan worden opgelost (dat wil zeggen, een niet-const alias kan bestaan die de status van het object kan wijzigen). Een ander (belangrijk) inzicht uit dit aliasing probleem: wijzen naar een object met een pointer-to-constgarandeert niet dat het object niet zal veranderen; het belooft alleen dat het object niet zal veranderen via die pointer.

Wat is de relatie tussen een return-by-reference en een const member function?

Als u een lid van uw this object wilt retourneren door verwijzing vanuit een inspector-methode, moet u het retourneren met reference-to-const (const X& inspect() const) of met waarde (X inspect() const).

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

het goede nieuws is dat de compiler u vaak zal betrappen als u dit verkeerd doet. In het bijzonder, als u per ongeluk een lid van uw this object terugstuurt met een niet-const referentie, zoals in Person::name_evil() hierboven, zal de compilerhet vaak detecteren en u een compile-time fout geven tijdens het compileren van de ingewanden van, in dit geval,Person::name_evil().

het slechte nieuws is dat de compiler je niet altijd zal vangen: er zijn gevallen waarin de compiler je gewoon nooit een foutmelding zal geven tijdens het compileren.

vertaling: je moet nadenken. Als dat je bang maakt, zoek dan een ander soort werk; “denken” is geen vierletterwoord.

onthoud de” const filosofie ” spread in deze sectie: een const lid functie mag niet veranderen (of een beller toestaan om te veranderen) de logische status van het this object (AKA abstract state AKA meaningwisestate). Denk aan wat een object betekent, niet hoe het intern wordt geïmplementeerd. De leeftijd en naam van een persoon zijn logischdeel van de persoon, maar de buurman en werkgever van de persoon zijn dat niet. Een inspector methode die een deel van de logische / abstracte / betekenisvolle toestand van het thisobject retourneert, mag geen niet-const pointer (of referentie) naar dat deel retourneren,ongeacht of dat deel intern is geïmplementeerd als een direct data-lid dat fysiek is ingebed in hetthis object of op een andere manier.

hoe zit het met “const-overloading”?

const overbelasting helpt u const juistheid te bereiken.

const overbelasting is wanneer u een inspector methode en een mutator methode hebt met dezelfde naam en hetzelfde aantal en type parameters. De twee verschillende methoden verschillen alleen in die zin dat de inspecteur const is en de mutator niet-const.

het meest voorkomende gebruik van const overbelasting is bij de subscript operator. U moet over het algemeen proberen een van de standaard containersjablonen te gebruiken, zoals std::vector, maar als u uw eigen klasse moet maken met een subscriptoperator, is hier de vuistregel: subscript operators komen vaak in paren.

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 // ...};

de const subscript operator geeft een const-referentie terug, zodat de compiler voorkomt dat bellers per ongeluk de Fredkunnen wijzigen. De non – const subscript operator geeft een non-const referentie terug, wat uw manier is om uw bellers (en de compiler) te vertellen dat uw bellers het Fred object mogen wijzigen.

wanneer een gebruiker van uw MyFredList klasse de subscript-operator aanroept, selecteert de compiler welke overbelasting hij moet aanroepen op basis van de Consten van zijn MyFredList. Als de beller een MyFredList a of MyFredList& a heeft, dan zal a de niet-const subscript operator oproepen, en de beller zal eindigen met een niet – const verwijzing naar een Fred:

stel bijvoorbeeld dat class Fred een inspector-methode inspect() const en een mutator-methode mutate()heeft:

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}

als de beller echter een const MyFredList a of const MyFredList& a heeft, dan zal a de const abonnee bellen, en de beller zal eindigen met een const verwijzing naar een Fred. Hierdoor kan de beller de Fredbij a inspecteren, maar wordt voorkomen dat de beller per ongeluk de Fred bij amuteert/wijzigt.

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}

Const overbelasting voor subscript-en funcall-operators wordt hier,hier, hier, hier en hier geïllustreerd.

u kunt natuurlijk ook const-overloading gebruiken voor andere dingen dan de subscript-operator.

Hoe kan het me helpen betere klassen te ontwerpen als ik logische toestand van fysieke toestand onderscheid?

omdat dit u aanmoedigt om uw klassen van buiten naar binnen te ontwerpen in plaats van van binnen naar buiten, wat op zijn beurt uw klassen en objecten gemakkelijker te begrijpen en te gebruiken, intuïtiever, minder foutgevoelig en sneller maakt. (Oke, dat is een lichte over-vereenvoudiging. Om alle als ‘s en’ s en maar ‘ s te begrijpen, moet je gewoon de rest van dit antwoord te lezen!)

laten we dit van binnenuit begrijpen-u zult (zou) uw klassen van buitenaf — in moeten ontwerpen, maar als u nieuw bent in dit concept, is het gemakkelijker om het van binnenuit te begrijpen.

aan de binnenkant hebben uw objecten een fysieke (of concrete of bitwise) toestand. Dit is de toestand die voor programmeurs gemakkelijk te zien en te begrijpen is; het is de toestand die er zou zijn als de klasse slechts EEN C-stijl structzou zijn.

aan de buitenkant hebben uw objecten gebruikers van uw klasse, en deze gebruikers zijn beperkt tot het gebruik van alleen public ledenfuncties en friends. Deze externe gebruikers zien het object ook als een status, bijvoorbeeld, als theobject van klasse Rectangle is met methoden width(), height() en area(), zullen uw gebruikers zeggen dat deze drie allemaal deel uitmaken van de logische (of abstracte of betekenisvolle) status van het object. Voor een externe gebruiker heeft het Rectangle object feitelijk een gebied, zelfs als dat gebied tijdens de vlucht wordt berekend (bijvoorbeeld als de methode area() het product van de breedte en hoogte van het object retourneert). In feite, en dit is het belangrijke punt, uw gebruikers niet weten en niet schelen hoe youimplement een van deze methoden; uw gebruikers zien nog steeds, vanuit hun perspectief, dat uw object logischerwijs een staat van breedte, hoogte en oppervlakte heeft.

het area() voorbeeld toont een geval waarin de logische toestand elementen kan bevatten die niet direct in de fysieke toestand worden gerealiseerd. Het tegenovergestelde is ook waar: klassen verbergen soms opzettelijk een deel van de fysieke(concrete, bitwise) status van hun objecten voor gebruikers — ze bieden opzettelijk geen public member functies offriends die gebruikers in staat zouden stellen om te lezen of schrijven of zelfs te weten over deze verborgen status. Dat betekent dat er bits zijn in de fysieke toestand van het object die geen overeenkomstige elementen hebben in de logische toestand van het object.

als voorbeeld van dit laatste geval kan een collectieobject zijn laatste lookup cachen in de hoop de prestaties van zijn volgende lookup te verbeteren. Deze cache maakt zeker deel uit van de fysieke toestand van het object, maar er is een intern implementatiedetail dat waarschijnlijk niet zal worden blootgesteld aan gebruikers — het zal waarschijnlijk geen deel uitmaken van de logische toestand van het object. Vertellen wat is wat is gemakkelijk als je denkt van buitenaf-in: als de gebruikers van het collection-object nu de status van de cache zelf moeten controleren, dan is de cache transparant en maakt het geen deel uit van de logicalstate van het object.

moet de Consten van mijn publieke member-functies gebaseerd zijn op wat de methode doet met de logische of fysieke toestand van het object?

logisch.

er is geen manier om dit volgende deel gemakkelijk te maken. Het gaat pijn doen. Beste aanbeveling is om te gaan zitten. En alsjeblieft, voor uw veiligheid, zorg ervoor dat er geen scherpe werktuigen in de buurt zijn.

laten we teruggaan naar het collection-object voorbeeld. Herinneren: er is een lookup methode die caches de laatste lookup in de hoop te versnellen toekomstige lookups.

laten we zeggen wat waarschijnlijk voor de hand ligt: neem aan dat de lookup methode geen wijzigingen aanbrengt in de logische status van het Collection-object.

dus … het is tijd om je pijn te doen. Ben je klaar?

hier komt: als de lookup methode geen verandering brengt in de logische status van het collection-object, maar de fysieke status van het collection-object verandert (het maakt een zeer reële verandering in de zeer reële cache), moet de lookup methode dan constzijn?

het antwoord is een volmondig ja. (Er zijn uitzonderingen op elke regel, dus ” ja ” moet echt een sterretje ernaast,maar de overgrote meerderheid van de tijd, het antwoord is ja.)

dit gaat allemaal over “logisch const “over” fysiek const.”Het betekent dat de beslissing over het al dan niet versieren van amethod met const in de eerste plaats moet afhangen van de vraag of die methode de logische staat ongewijzigd laat, ongeacht (zit je neer?) (je zou kunnen gaan zitten) ongeacht of de methode toevallig zeer reële veranderingen maakt aan de zeer reële fysieke toestand van het object.

in het geval dat niet ingezakt, of in het geval u nog geen pijn, laten we plagen het uit elkaar in twee gevallen:

  • als een methode een deel van de logische toestand van het object verandert, is het logischerwijs een mutator; het zou niet const evenif moeten zijn (zoals in feite gebeurt!) de methode verandert geen fysieke bits van de concrete toestand van het object.
  • omgekeerd is een methode logischerwijs een inspecteur en zou const moeten zijn als het nooit enig deel van de logische toestand van het object verandert, zelfs als (zoals in werkelijkheid gebeurt! de methode verandert de fysieke bits van de concrete toestand van het object.

als u in de war bent, lees het nogmaals.

als je niet in de war bent, maar boos bent, goed: je vindt het misschien nog niet leuk, maar je begrijpt het tenminste. Haal diep adem en herhaal na mij: “de constwaarde van een methode moet zinvol zijn van buiten het object.”

als je nog steeds boos bent, herhaal dit dan drie keer: “de Consten van een methode moet zinvol zijn voor de gebruikers van het object, en die gebruikers kunnen alleen de logische status van het object zien.”

als je nog steeds boos bent, sorry, het is wat het is. Accepteer het en leef ermee. Ja, Er zullen uitzonderingen zijn; elke regel heeft ze. Maar in de regel is dit logische const begrip goed voor u en goed voor uw software.

nog iets. Dit gaat zinloos worden, maar laten we precies zijn of een methode de logicalstate van het object verandert. Als je buiten de klasse bent-je bent een normale gebruiker, elk experiment dat je zou kunnen uitvoeren (elke methode ofvolgorde van methoden die je aanroept) zou dezelfde resultaten hebben (dezelfde return waarden, dezelfde uitzonderingen of het ontbreken van uitzonderingen)ongeacht of je voor het eerst die lookup methode hebt aangeroepen. Als de lookup functie veranderd elk toekomstig gedrag van elke toekomstige methode (niet alleen waardoor het sneller, maar veranderde de uitkomst, veranderde de return waarde, veranderde theexception), dan is de lookup methode veranderd logische toestand van het object — het is een mutuator. Maar als de lookup methode niets anders veranderd dan misschien het maken van sommige dingen sneller, dan is het een Inspecteur.

Wat moet ik doen als Ik wil dat een const-member-functie een “onzichtbare” wijziging maakt in een datalid?

gebruik mutable (of gebruik in laatste instantie const_cast).

een klein percentage inspecteurs moet wijzigingen aanbrengen in de fysieke toestand van een object die niet door externalusers kunnen worden waargenomen — wijzigingen in de fysieke maar niet logische toestand.

bijvoorbeeld, het eerder besproken collectieobject boekte zijn laatste lookup in de cache in de hoop de prestaties van zijn volgende lookup te verbeteren. Aangezien de cache, in dit voorbeeld, niet direct kan worden waargenomen door een deel van de openbare interface van het collection-object (anders dan timing), zijn het bestaan en de toestand ervan geen deel van de logische toestand van het object, dus wijzigingen zijn onzichtbaar voor externe gebruikers. De opzoekmethode is een inspector omdat deze nooit de logische toestand van het object verandert, ongeacht het feit dat het, althans voor de huidige implementatie, de fysieke toestand van het object verandert.

wanneer methoden de fysische maar niet logische toestand wijzigen, moet de methode in het algemeen worden gemarkeerd als const omdat het echt een inspectormethode is. Dat levert een probleem op: wanneer de compiler uw const methode ziet die de fysieke status van het this object verandert, zal het klagen — het zal uw code een foutmelding geven.

de C++ compiler taal gebruikt het mutable sleutelwoord om u te helpen deze logische const notie te omarmen. In dit geval zou je de cache markeren met het mutable sleutelwoord,op die manier weet de compiler dat het toegestaan is om te veranderen binnen eenconst methode of via een andere const pointer of referentie. In onze lingo markeert het mutable sleutelwoord de porties van de fysieke toestand van het object die geen deel uitmaken van de logische toestand.

het mutable sleutelwoord gaat net voor de declaratie van het data lid, dat wil zeggen, dezelfde plaats waar uconstzou kunnen plaatsen. De andere aanpak niet de voorkeur, is om weg te werpen de const‘heid van de this pointer, waarschijnlijk via deconst_cast zoekwoorden:

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

Na deze regel, self dezelfde bits als this, dat is, self == this, maar self is een Set* in plaats van eenconst Set* (technisch this is een const Set* const, maar de meest rechtse const is niet relevant voor deze discussie).Dat betekent dat u self kunt gebruiken om het object waarnaar wordt verwezen met thiste wijzigen.

Opmerking: Er is een zeer onwaarschijnlijke fout die kan optreden met const_cast. Het gebeurt alleen wanneer drie zeer zeldzaamheden tegelijkertijd worden gecombineerd: een data-lid dat mutable zou moeten zijn( zoals hierboven is besproken), een compilerdie het mutable sleutelwoord niet ondersteunt en/of een programmeur die het niet gebruikt, en een object dat oorspronkelijk gedefinieerd was als const (in tegenstelling tot een normaal, niet-const object dat wordt aangeduid met een pointer-to-const).Hoewel deze combinatie zo zeldzaam is dat het je misschien nooit zal overkomen, werkt de code mogelijk niet (de standaard zegt dat het gedrag niet gedefinieerd is).

als u ooit const_cast wilt gebruiken, gebruik dan mutable. Met andere woorden, als u ooit een lid van een object moet veranderen en naar dat object wordt verwezen met een pointer-to-const, is het veiligste en eenvoudigste om mutable toe te voegen aan de ledenverklaring. U kunt const_cast gebruiken als u er zeker van bent dat het object niet const is (bijvoorbeeld als u er zeker van bent dat het object ongeveer als volgt wordt gedeclareerd: Set s;), maar als het object zelf const kan zijn (bijvoorbeeld als het kan worden gedeclareerd als: const Set s;), gebruik dan mutable in plaats van const_cast.

schrijf niet als je zegt dat Versie X van compiler Y op machine Z je een niet-mutable lid van eenconst object laat veranderen. Het kan me niet schelen — Het is illegaal volgens de taal en uw code zal waarschijnlijk mislukken op een andere compiler of zelfs een andere versie (een upgrade) van dezelfde compiler. Zeg gewoon nee. Gebruik in plaats daarvan mutable. Schrijf codedat gegarandeerd werkt, geen code die niet lijkt te breken.

betekent const_cast gemiste optimalisatiemogelijkheden?

in theorie ja; in de praktijk Nee.

zelfs als de taal verboden is const_cast, zou de enige manier om te voorkomen dat de register-cache over een const memberfunction-aanroep wordt gespoeld, zijn om het aliasprobleem op te lossen (d.w.z., om te bewijzen dat er geen niet – const pointers zijn die naar het object verwijzen). Dit kan alleen in zeldzame gevallen gebeuren (wanneer het object geconstrueerd is in het kader van de const ledenfunctie aanroep, en wanneer alle niet – const ledenfunctie aanroepingen tussen de constructie van het object en de const ledenfunctie aanroep statisch gebonden zijn, en wanneer elk van deze aanroepingen ook inlined is, en wanneer de constructor zelf inlined is, en wanneer alle ledenfuncties de aanroep van de constructor inlinezijn).

Waarom staat de compiler me toe om een int te wijzigen nadat ik er met een const int*naar heb gewezen?

omdat ” const int* p “betekent” p belooft de *p niet te veranderen, “not” *p belooft niet te veranderen.”

waardoor een const int* naar een int wijst, betekent niet const-ify de int. De int kan niet worden gewijzigd via deconst int*, maar als iemand anders een int* (opmerking: no const) heeft die verwijst naar (“aliassen”) dezelfde int, dan kan dieint* worden gebruikt om de intte wijzigen. Bijvoorbeeld:

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!) // ...}

merk op dat main() en f(const int*,int*) in verschillende compilatie-eenheden kunnen zijn die op verschillende dagen van de week worden samengesteld. In dat geval is er geen enkele manier waarop de compiler de aliasing kan detecteren tijdens het compileren. Daarom kunnen we geen taalregel maken die dit soort dingen verbiedt. In feite zouden we niet eens zo ‘ n arule willen maken, omdat het in het algemeen wordt beschouwd als een functie die je veel aanwijzers kunt hebben die naar hetzelfde ding wijzen. Het feit dat een van die pointers belooft het onderliggende “ding” niet te veranderen is gewoon een belofte gemaakt door de pointer; het is geen belofte gemaakt door het “ding”.

betekent” const Fred* p ” dat * p niet kan veranderen?

Nee! (Dit is gerelateerd aan de FAQ over aliasing van int pointers.)

const Fred* p” betekent dat de Fred niet kan worden gewijzigd via pointer p, maar er kunnen andere manieren zijn om bij het object te komen zonder door eenconst te gaan (zoals een aliased niet-const pointer zoals een Fred*). Als u bijvoorbeeld twee pointers “const Fred* p” en “Fred* q” hebt die naar hetzelfde Fred object (aliasing) wijzen, kan pointer q worden gebruikt om het Fred object te wijzigen, maar pointer p niet.

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 // ...}

Waarom krijg ik een fout bij het converteren van een Foo* * → const Foo**?

omdat het omzetten van Foo**const Foo** ongeldig en gevaarlijk zou zijn.

C++ staat de (veilige) conversie Foo*Foo const* toe, maar geeft een fout als u impliciet Foo**const Foo**probeert te converteren.

de reden waarom die fout een goede zaak is wordt hieronder gegeven. Maar eerst is hier de meest voorkomende oplossing: simplychange const Foo** naar const Foo* const*:

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* // ...}

de reden dat de conversie vanaf Foo**const Foo** gevaarlijk is, is dat het u in stilte en per ongeluk een const Foo object laat wijzigen zonder een cast:

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!! // ...}

als de lijn q = &p legaal zou zijn, zou q wijzen op p. De volgende regel, *q = &x, verandert p zelf (aangezien *qp is) om te wijzen op x. Dat zou een slechte zaak zijn, omdat we de const qualifier zouden hebben verloren: p is een Foo* maarx is een const Foo. De p->modify() lijn maakt gebruik van p ‘ s mogelijkheid om de referent te wijzigen,wat het echte probleem is, omdat we uiteindelijk een const Foohebben gewijzigd.Als u een crimineel onder een wettige vermomming verbergt, dan kan hij het vertrouwen dat aan die vermomming is gegeven, uitbuiten.Dat is slecht.

Gelukkig voorkomt C++ dat u dit doet: de regel q = &p wordt door de compiler gemarkeerd als een compile-time fout. Herinnering: please do not pointer-cast uw weg rond die compile-time foutmelding. Zeg Gewoon Nee!

(Opmerking: Er is een begripsmatige overeenkomst tussen dit en het verbod om Derived** om te zetten inBase**.)

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.