Standard C++

Const Korrekthet

Hva er “const korrekthet”?

en god ting. Det betyr å bruke søkeordet const for å hindre const objekter fra å bli mutert.

hvis du for eksempel ønsket å opprette en funksjon f() som aksepterte en std::string, pluss at du vil love innringereikke endre innringerens std::string som blir sendt til f(), kan du få f()motta parameteren std::string

  • void f1(const std::string& s); // Pass ved referanse-til-const
  • void f2(const std::string* sptr); // Pass av pekeren-til-const
  • void f3(std::string s); // Pass by value

i pass by reference-to-const og pass by pointer-to-const cases, vil alle forsøk på å endre innringerensstd::string i f() – funksjonene bli flagget av kompilatoren som en feil på kompileringstid. Denne sjekken er fullstendig på kompileringstid: det er ingen kjøretidsrom eller hastighetskostnad for const. I pass by value-saken (f3()) får den oppkalte funksjonen en kopi av innringerens std::string. Dette betyr at f3() kan endre sin localcopy, men kopien er ødelagt når f3() returnerer. Spesielt f3() kan ikke endre innringerens std::stringobjekt.

som et motsatt eksempel, anta at du ønsket å opprette en funksjon g() som aksepterte en std::string, men du vil lacallers vite at g() kan endre innringerens std::string – objekt. I dette tilfellet kan du ha g() motta sin std::string parameter…

  • void g1(std::string& s); // Pass ved referanse-til-ikke-const
  • void g2(std::string* sptr); // Restauranter i nærheten av pointer-to-non-const

mangelen på const i disse funksjonene forteller kompilatoren at de har lov til (men er ikke pålagt å) endre thecaller ‘ s std::string objekt. Dermed kan de passere sine std::string til noen av funksjonene f(), men bare f3()(den som mottar sin parameter “etter verdi”) kan passere sin std::string til g1() eller g2(). Hvis f1() eller f2()trenger å ringe enten g() – funksjonen, må en lokal kopi av std::string – objektet sendes til g() – funksjonen; theparameter til f1() eller f2() kan ikke sendes direkte til enten g() – funksjonen. F. eks.,

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}

Naturligvis i ovennevnte tilfelle gjøres eventuelle endringer som g1() gjør, til localCopy – objektet som er lokalt til f1(). spesielt vil det ikke bli gjort noen endringer i const – parameteren som ble sendt med referanse til f1().

Hvordan er” const korrekthet ” relatert til vanlig typesikkerhet?

Deklarere const-ness av en parameter er bare en annen form for typesikkerhet.

hvis du finner vanlig type sikkerhet hjelper deg med å få systemer riktig (det gjør det, spesielt i store systemer), finner duconst korrekthet hjelper også.

fordelen med const korrekthet er at den forhindrer deg i å utilsiktet endre noe du ikke forventet å bli endret. Du ender opp med å måtte dekorere koden din med noen ekstra tastetrykk (nøkkelordet const), med fordel at du forteller kompilatoren og andre programmerere noe ekstra viktig semantisk informasjon-informasjon som kompilatoren bruker for å forhindre feil og andre programmerere bruker som dokumentasjon.

Konseptuelt kan du forestille deg at const std::string for eksempel er en annen klasse enn vanlig std::string, siden const – varianten konseptuelt mangler de forskjellige mutative operasjonene som er tilgjengelige i ikke – const – varianten. Du kan for eksempel konseptuelt forestille deg at en const std::string ganske enkelt ikke har en oppdragsoperatør+= eller andre mutative operasjoner.

Skal jeg prøve å få ting til å være riktig ” før “eller”senere”?

Helt, veldig, helt i begynnelsen.

Back-patching const korrekthet resulterer i en snøballeffekt: hver const du legger til “her” krever fire merå bli lagt til ” der borte.”

Legg til const tidlig og ofte.

hva betyr” const X* p”?

det betyr p poeng til et objekt av klasse X, men p kan ikke brukes til å endre det X objektet (naturlig p kan også NULL).

Les det høyre mot venstre: “p er en peker Til En x som er konstant.”

for eksempel, hvis klasse X har en const medlemsfunksjon som inspect() const, er det greit å sip->inspect(). Men hvis klasse X har en ikke – const medlemsfunksjon kalt mutate(), er det anerror hvis du sier p->mutate().

Betydelig, er denne feilen fanget opp av kompilatoren på compile-time-ingen run – time tester er gjort. Det betyr const ikke tregere programmet og krever ikke at du skriver ekstra testtilfeller for å sjekke ting på runtime-thecompiler gjør arbeidet på kompilere-tid.

hva er forskjellen mellom “const X* p”, “X * const p” og “const X * const p”?

Les pekerdeklarasjonene fra høyre til venstre.

  • const X* p betyr ” p peker på en X som er const“: X – objektet kan ikke endres via p.
  • X* const p betyr ” p er en const peker til en X som ikke er-const“: du kan ikke endre pekeren p selv, men du kan endre X – objektet via p.
  • const X* const p betyr “p er en const peker til en Xsom er const“: du kan ikke endre pekeren pselv, og du kan heller ikke endre X – objektet via p.

og, oh ja, nevnte jeg å lese pekererklæringene dine fra høyre til venstre?

hva betyr” const X& x”?

det betyr x aliaser et X objekt, men du kan ikke endre det X objektet via x.

Les det høyre mot venstre: “x er en referanse til en X som er const.”

for eksempel, hvis klasse X har en const medlemsfunksjon som inspect() const, er det greit å six.inspect(). Men hvis klasse X har en ikke – const medlemsfunksjon kalt mutate(), er det en feil hvis du sier x.mutate().

Dette er helt symmetrisk med pekere til const, inkludert det faktum at kompilatoren gjør all kontroll ved kompileringstid, noe som betyr at const ikke senker programmet ditt og krever ikke at du skriver ekstra testtilfeller for å sjekke ting under kjøring.

hva betyr” X const& x “og” X const* p”?

X const& x tilsvarer const X& x, og X const* x tilsvarerconst X* x.

noen foretrekker const – på-høyre stil, kaller det “konsekvent const” eller, Ved hjelp Av Et begrep laget Av Simon Brand, ” Øst const.”Faktisk Kan” Øst const ” – stilen være mer konsistent enn alternativet:” Øst const ” – stilen setter alltid const til høyre for hva den bekrefter, mens den andre stilen noen ganger setter const til venstre og noen ganger til høyre (for const pekerdeklarasjoner og const medlemsfunksjoner).

med Stilen “Øst const” defineres en lokal variabel som er const med const til høyre:int const a = 42;. Tilsvarende er en static variabel som er const definert som static double const x = 3.14;.I utgangspunktet ender hver const til høyre for tingen den godkjenner, inkludert const som kreves for å være til høyre: const pekerdeklarasjoner og med en const medlemsfunksjon.

Stilen “Øst const” er også mindre forvirrende når den brukes med typealiaser: Hvorfor har foo og bar forskjellige typer her?

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

Bruk av” Øst const ” – stilen gjør dette klarere:

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

det er tydeligere her at foo og foobar er samme type og at bar er en annen type.

Stilen “Øst const” er også mer konsistent med pekerdeklarasjoner. Kontrast den tradisjonelle stilen:

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

Med “Øst const” – stilen

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

Til tross for disse fordelene, er const – på-høyre-stilen ennå ikke populær, så legacy code har en tendens til å ha den tradisjonelle stilen.

gjør “X& const x” noe fornuftig?

Nei, det er tull.

for å finne ut hva erklæringen ovenfor betyr, les den fra høyre til venstre: “xer en constreferanse til en X“. Men det er overflødig — referanser er alltid const, i den forstand at du aldri kan gjenopprette areference for å få det til å referere til et annet objekt. Aldri. Med eller uten const.

med andre ord,” X& const x “er funksjonelt ekvivalent med” X& x“. Siden du ikke får noe ved å legge tilconst etter &, bør du ikke legge til det: det vil forvirre folk — const vil få noen til å tro at X er const, som om du hadde sagt ” const X& x“.

Hva er en “const-medlemsfunksjon”?

en medlemsfunksjon som inspiserer (i stedet for muterer) objektet.

en const medlemsfunksjon er angitt med et const – suffiks like etter medlemsfunksjonens parameterliste. Medlemsfunksjoner med suffikset const kalles “const medlemsfunksjoner “eller” inspektører.”Medlemsfunksjoner uten suffiksconst kalles” ikke – const medlemsfunksjoner “eller” mutatorer.”

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}

forsøket på å ringe unchangeable.mutate() er en feil fanget ved kompileringstid. Det er ingen runtime space eller speedpenalty for const, og du trenger ikke å skrive testtilfeller for å sjekke det under kjøring.

den etterfølgende constinspect() medlemsfunksjonen skal brukes til å bety at metoden ikke vil endre object ‘ sabstract (client-visible) – tilstanden. Det er litt annerledes enn å si at metoden ikke vil endre “råbiter” av theobjects struct. C++ – kompilatorer har ikke lov til å ta” bitvis ” tolkning med mindre de kan løse thealiasing-problemet, som normalt ikke kan løses (dvs.et ikke-const alias kan eksistere som kan endre tilstanden til objektet). En annen (viktig) innsikt fra dette aliasing problemet: å peke på et objekt med en peker-til-const garanterer ikke at objektet ikke vil endres; det lover bare at objektet ikke vil endres via den pekeren.

Hva er forholdet mellom en retur-for-referanse og en const-medlemsfunksjon?

hvis du vil returnere et medlem av this – objektet ved referanse fra en inspektørmetode, bør du returnere det ved hjelp av reference-to-const (const X& inspect() const) eller etter verdi (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!!}

den gode nyheten er at kompilatoren ofte vil fange deg hvis du tar feil. Spesielt hvis du ved et uhell returnerer et medlem av this – objektet ved ikke – const referanse, for eksempel i Person::name_evil() ovenfor, vil kompilatoren ofte oppdage det og gi deg en kompileringstidsfeil mens du samler innmaten av, i dette tilfelletPerson::name_evil().

den dårlige nyheten er at kompilatoren ikke alltid vil fange deg: det er noen tilfeller der kompilatoren rett og slett ikke vil gi deg en kompileringstid feilmelding.

Oversettelse: Du må tenke. Hvis det skremmer deg, finn en annen arbeidslinje; “tenk” er ikke et ord med fire bokstaver.

Husk “const filosofi ” spredt over hele denne delen: en const medlemsfunksjon må ikke endre (eller tillate en innringer å endre) this objektets logiske tilstand (AKA abstrakt tilstand AKA meaningwisestate). Tenk på hva et objekt betyr, ikke hvordan det er internt implementert. En Persons alder og navn er logiskdel Av Personen, Men Personens nabo og arbeidsgiver er ikke. En inspektørmetode som returnerer en del av this – objektets logiske / abstrakte / meningsfulle tilstand, må ikke returnere en ikke – const – peker (eller referanse) til den delen,uavhengig av om den delen er internt implementert som et direkte datamedlem fysisk innebygd ithis-objektet eller på annen måte.

Hva er avtalen med “const-overbelastning”?

const overbelastning hjelper deg med å oppnå const korrekthet.

const overbelastning er når du har en inspektørmetode og en mutatormetode med samme navn og samme antall og typer parametere. De to forskjellige metodene skiller seg bare ved at theinspector er const og mutatoren er ikke – const.

den vanligste bruken av const overbelastning er med subscript-operatoren. Du bør vanligvis prøve å bruke en avstandard container maler, for eksempel std::vector, men hvis du trenger å lage din egen klasse som har en subscriptoperator, her er tommelfingerregelen: subscript operatører kommer ofte i par.

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

subscript-operatoren const returnerer en const-referanse, slik at kompilatoren forhindrer innringere fra utilsiktet endring/endring av Fred. Subscript-operatoren som ikke erconst returnerer en referanse som ikke erconst, som er din måte å fortelle innringerne dine (og kompilatoren) at innringerne dine har lov til å endre Fred – objektet.

når en bruker av MyFredList – klassen kaller subscript-operatoren, velger kompilatoren hvilken overbelastning som skal kalles basert på konstansen av deres MyFredList. Hvis den som ringer har en MyFredList a eller MyFredList& a, vila ringe til operatøren som ikke erconst, og den som ringer, vil ende opp med en ikke- const referanse til en Fred:

anta for eksempel at class Fred har en inspektør-metode inspect() const og en mutator-metode 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}

men hvis den som ringer har en const MyFredList a eller const MyFredList& a, vil a ringe const subscriptoperator, og den som ringer, vil ende opp med en const referanse til en Fred. Dette gjør det mulig for den som ringer å inspisere Fred ved a, men det forhindrer at den som ringer utilsiktet muterer /endrer Fred ved a.

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 overbelastning for subscript – og funcall-operatører er illustrert her, her, her, her og her.

du kan selvfølgelig også bruke const – overbelastning for andre ting enn abonnementsoperatøren.

Hvordan kan det hjelpe meg med å designe bedre klasser hvis jeg skiller logisk tilstand fra fysisk tilstand?

fordi det oppfordrer deg til å designe klassene dine fra utsiden-inn i stedet for fra innsiden-ut, noe som igjen gjør klassene og objektene enklere å forstå og bruke, mer intuitive, mindre feilutsatte og raskere. (Ok, det er en liten overforenkling. For å forstå alle if ‘s og’ s og men ‘ s, må du bare lese resten av thisanswer!)

La oss forstå dette fra innsiden ut-du vil (burde) designe klassene dine utenfra, men hvis du er ny på dette konseptet, er det lettere å forstå fra innsiden ut.

på innsiden har objektene dine fysisk (eller betong eller bitvis) tilstand. Dette er staten som er lett for programmererå se og forstå; det er staten som ville være der hvis klassen bare var En C-stil struct.

på utsiden har objektene dine brukere av klassen din, og disse brukerne er begrenset til å bruke bare public medlemsfunksjoner og friends. Disse eksterne brukerne oppfatter også objektet som å ha tilstand, for eksempel hvis objektet er av klasse Rectangle med metoder width(), height() og area(), vil brukerne si at de tre er alle en del av objektets logiske (eller abstrakte eller meningsfulle) tilstand. Til en ekstern bruker har Rectangle objectactually et område, selv om området er beregnet på fly (f. eks hvis area() – metoden returnerer produktet av objektets bredde og høyde). Faktisk, og dette er viktig poeng, brukerne ikke vet og ikke bryr seg hvordan youimplement noen av disse metodene; brukerne fortsatt oppfatter, fra deres perspektiv, at objektet logisk har ameaningwise tilstand av bredde, høyde og areal.

eksemplet area() viser et tilfelle der den logiske tilstanden kan inneholde elementer som ikke er direkte realisert i fysisk tilstand. Det motsatte er også sant: klasser skjuler noen ganger bevisst en del av objektets fysiske (konkrete, bitvis) tilstand fra brukere – de gir med vilje ikke noen public medlemsfunksjoner eller friend s som vil tillate brukere å lese eller skrive eller til og med vite om denne skjulte tilstanden. Det betyr at det er biter i objektets fysiske tilstand som ikke har noen tilsvarende elementer i objektets logiske tilstand.

som et eksempel på dette sistnevnte tilfellet, kan et samlingsobjekt cache sitt siste oppslag i håp om å forbedre ytelsen til sitt neste oppslag. Denne cachen er absolutt en del av objektets fysiske tilstand, men der er det en internimplementeringsdetalj som sannsynligvis ikke vil bli utsatt for brukere – det vil nok ikke være en del av objektets logiske tilstand. Å fortelle hva som er det som er lett hvis du tenker fra utsiden-inn: hvis samlingen-objektets brukere har noway å sjekke tilstanden til cache selv, så cache er gjennomsiktig, og er ikke en del av objektets logicalstate.

skal konstansen av mine offentlige medlemsfunksjoner være basert på hva metoden gjør med objektets logiske tilstand eller fysiske tilstand?

Logisk.

Det er ingen måte å gjøre denne neste delen lett. Det kommer til å gjøre vondt. Den beste anbefalingen er å sette seg ned. Og vær så snill, for din sikkerhet, sørg for at det ikke er skarpe redskaper i nærheten.

La oss gå tilbake til samlingsobjekteksemplet. Huske: det er en oppslag metode thatcaches siste oppslag i håp om å få fart på fremtidige oppslag.

la oss angi hva som sannsynligvis er åpenbart: anta at oppslagsmetoden ikke gjør noen endringer i noen av samlingsobjektets logiske tilstand.

så … tiden er kommet for å skade deg. Er du klar?

Her kommer: hvis oppslagsmetoden ikke gjør noen endring i noen av samlingsobjektets logiske tilstand, men det endrer samlingsobjektets fysiske tilstand (det gjør en veldig reell endring til den veldig virkelige cachen), bør oppslagsmetoden være const?

svaret Er et rungende Ja. (Det er unntak for hver regel, så ” Ja ” burde virkelig ha en stjerne ved siden av den, men det store flertallet av tiden er svaret Ja.)

dette handler om “logisk const “over” fysisk const.”Det betyr at beslutningen om å dekorere amethod med const bør hengsle primært på om den metoden forlater den logiske tilstanden uendret, uansett(setter du deg ned?) (du vil kanskje sette deg ned) uavhengig av om metoden skjer for å gjøre veldig virkelige endringer i objektets veldig virkelige fysiske tilstand.

hvis det ikke synker inn, eller hvis du ikke er i smerte, la oss plage det i to tilfeller:

  • hvis en metode endrer noen del av objektets logiske tilstand, er det logisk en mutator; det bør ikke være const evenif (som faktisk skjer!) metoden endrer ikke noen fysiske biter av objektets konkrete tilstand.
  • Omvendt er en metode logisk en inspektør og bør være const hvis den aldri endrer noen del av objektets logiske tilstand, selv om (som faktisk skjer!) metoden endrer fysiske biter av objektets konkrete tilstand.

hvis du er forvirret, les den igjen.

hvis du ikke er forvirret, men er sint, bra: du kan ikke like det ennå, men i det minste forstår du det. Ta et dypt pustog gjenta etter meg: “const ness av en metode bør være fornuftig fra utsiden av objektet.”

hvis du fortsatt er sint, gjenta dette tre ganger: “konstansen til en metode må være fornuftig for objektets brukere, og disse brukerne kan bare se objektets logiske tilstand.”

hvis du fortsatt er sint, beklager, det er hva det er. Sug det opp og leve med det. Ja, det vil være unntak; hver regelhar dem. Men som regel er dette logiske const konseptet bra for deg og bra for programvaren din.

En ting til. Dette kommer til å bli inane, men la oss være presise om en metode endrer objektets logicalstate. Hvis du er utenfor klassen — du er en vanlig bruker, vil hvert eksperiment du kan utføre (hver metode orsequence av metoder du ringer) ha de samme resultatene (samme returverdier, samme unntak eller mangel på unntak)uansett om du først ringte den oppslagsmetoden. Hvis oppslagsfunksjonen endret enhver fremtidig oppførsel av enhver fremtidig metode (ikke bare å gjøre det raskere, men endret utfallet, endret returverdien, endret exception), endret oppslagsmetoden objektets logiske tilstand — det er en mutuator. Men hvis oppslag methodchanged ingenting annet enn kanskje å gjøre noen ting raskere, så er det en inspektør.

Hva gjør jeg hvis jeg vil at en const-medlemsfunksjon skal gjøre en” usynlig ” endring til et datamedlem?

Bruk mutable(eller, som en siste utvei, bruk const_cast).

en liten prosentandel av inspektørene må gjøre endringer i objektets fysiske tilstand som ikke kan observeres av eksterne brukere-endringer i den fysiske, men ikke logiske tilstanden.

for eksempel bufret samlingsobjektet tidligere sitt siste oppslag i håp om å forbedre ytelsen til det neste oppslaget. Siden hurtigbufferen i dette eksemplet ikke kan observeres direkte av noen del av samlingsobjektets offentlige grensesnitt (annet enn timing), er dens eksistens og tilstand ikke en del av objektets logiske tilstand, så endringer i den er usynlige for eksterne brukere. Oppslagsmetoden er en inspektør siden den aldriendrer objektets logiske tilstand, uavhengig av det faktum at det i hvert fall for den nåværende implementeringen endrer objektets fysiske tilstand.

når metoder endrer den fysiske, men ikke logiske tilstanden, bør metoden generelt merkes som const siden det virkelig er en inspektørmetode. Det skaper et problem: når kompilatoren ser din const – metode som endrer fysisk tilstand av this – objektet, vil det klage — det vil gi koden en feilmelding.

c++ – kompilatorspråket bruker mutable søkeordet for å hjelpe deg med å omfavne denne logiske const oppfatningen. I dette tilfellet vil du merke cachen med nøkkelordet mutable, slik at kompilatoren vet at det er tillatt å endre seg i en const – metode eller via en annen const peker eller referanse. I vår lingo markerer mutable søkeordet de delene av objektets fysiske tilstand som ikke er en del av den logiske tilstanden.

nøkkelordet mutable går like før datamedlemmets erklæring, det vil si det samme stedet der du kan setteconst. Den andre tilnærmingen, ikke foretrukket, er å kaste bort const ‘ ness av this pekeren, sannsynligvis via const_cast søkeordet:

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

etter denne linjen vil self ha de samme bitene som this, det vil si self == this, men self er en Set* i stedet for enconst Set* (teknisk this er en const Set* const, men den høyeste const er irrelevant for denne diskusjonen).Det betyr at du kan bruke self for å endre objektet pekte på this.

MERK: det er en ekstremt usannsynlig feil som kan oppstå med const_cast. Det skjer bare når tre svært sjeldneting kombineres samtidig: et datamedlem som burde være mutable (som omtalt ovenfor), en kompilert som ikke støtter mutable – nøkkelordet og/eller en programmerer som ikke bruker det, og et objekt som opprinnelig var definert til å være const (i motsetning til et normalt, ikke-const-objekt som pekes til av en peker-til – const).Selv om denne kombinasjonen er så sjelden at det aldri kan skje med deg, hvis det noen gang skjedde, kan koden ikke fungere (standarden sier at oppførselen er udefinert).

hvis du noen gang vil bruke const_cast, bruk mutable i stedet. Med andre ord, hvis du noen gang trenger å endre et medlem av anobject, og det objektet peker på med en peker-til-const, er det sikreste og enkleste å legge til mutable til medlemmets erklæring. Du kan bruke const_cast hvis du er sikker på at det faktiske objektet ikke er const (f.eks. hvis du er sikker på at objektet er erklært noe slikt: Set s;), men hvis selve objektet kan være const (f. eks. hvis det kan bli erklært som: const Set s;), bruk mutable i stedet for const_cast.

Vennligst ikke skriv si versjon X av kompilatoren Y på maskin Z lar deg endre et ikke-mutable medlem av etconst objekt. Jeg bryr meg ikke – det er ulovlig i henhold til språket, og koden din vil trolig mislykkes på en annen kompilator eller til og med en annen versjon (en oppgradering) av samme kompilator. Bare si nei. Bruk mutable i stedet. Skriv kodesom er garantert å fungere, ikke kode som ikke ser ut til å bryte.

betyr const_cast tapte optimaliseringsmuligheter?

i teorien, ja; i praksis, nei.

selv om språket forbød const_cast, vil den eneste måten å unngå å spyle registerbufferen over et const medlemsfunksjonskall være å løse aliasproblemet (dvs ., for å bevise at det ikke er noen ikke-const pekere som peker på objektet). Dette kan bare skje i sjeldne tilfeller (når objektet er konstruert i omfanget av const medlem funksjon påkalling, og når alle ikke – const medlem funksjon påkallinger mellom objektets konstruksjon ogconst medlem funksjon påkalling er statisk bundet, og når hver og en av disse påkallinger er også inline d, og når konstruktøren selv er inline d, og når et medlem funksjoner konstruktør kall er inline).

Hvorfor tillater kompilatoren meg å endre en int etter at jeg har pekt på den med en const int*?

Fordi “const int* p “betyr” p lover ikke å endre *p, “ikke” *p lover ikke å endre.”

Forårsaker en const int* å peke på en int ikke const-ify int. int kan ikke endres via const int*, men hvis noen andre har en int*(merk: nei const) som peker på (“aliaser”) det samme int, kan detint* brukes til å endre int. Eksempelvis:

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 at main() og f(const int*,int*) kan være i forskjellige kompileringsenheter som er samlet på forskjellige dager i uken. I så fall er det ingen måte kompilatoren kan muligens oppdage aliasing på kompileringstid. Derfor er det ingen måte vi kunne lage en språkregel som forbyder denne typen ting. Faktisk ville vi ikke engang ønske å lage slike arule, siden det generelt betraktes som en funksjon som du kan ha mange poeng som peker på det samme. Det faktum at en av disse pekerne lover ikke å endre den underliggende “tingen” er bare et løfte fra pekeren; det er ikke et løfte laget av “tingen”.

betyr” const Fred* p ” at * p ikke kan endres?

Nei! (Dette er relatert TIL FAQ om aliasing av int pekere.)

const Fred* p” betyr at Fred ikke kan endres via pekeren p, men det kan være andre måter å komme på theobject uten å gå gjennom en const (for eksempel en aliasert ikke-const peker som en Fred*). Hvis du for eksempel har to pekere “const Fred* p” og “Fred* q” som peker til det samme Fred – objektet (aliasing), kan pekeren q brukes til å endre Fred – objektet, men pekeren p kan ikke.

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

Hvorfor får jeg en feilmelding om å konvertere En foo * * → const Foo**?

fordi konvertering Foo**const Foo** ville være ugyldig og farlig.

C++ tillater (sikker) konvertering Foo*Foo const* , men gir en feil hvis du prøver å implisitt konvertere Foo**const Foo**.

begrunnelsen for hvorfor denne feilen er en god ting er gitt nedenfor. Men først, her er den vanligste løsningen: simplychange const Foo** til 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* // ...}

grunnen til at konverteringen fra Foo**const Foo** er farlig er at det ville la deg stille og ved et uhell endre et const Foo objekt uten kast:

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

hvis q = &p – linjen var lovlig, ville q peke på p. Den neste linjen, *q = &x, endrer p selv (siden *q er p) for å peke på x. Det ville være en dårlig ting, siden vi ville ha mistet const kvalifikatoren: p er en Foo* men x er en const Foo. p->modify() – linjen utnytter p ‘ s evne til å endre referenten, som er det virkelige problemet, siden vi endte med å endre en const Foo.

ved analogi, hvis du skjuler en kriminell under lovlig forkledning, kan han da utnytte tilliten til den forkledningen.Det er ille.

Heldigvis Hindrer C++ deg i å gjøre dette: linjen q = &p er flagget av c++ – kompilatoren som en kompileringstidsror. Påminnelse: vennligst ikke pointer-cast deg rundt som kompilere tid feilmelding. Bare Si Nei!

(Merk: det er en konseptuell likhet mellom dette og forbudet mot å konvertere Derived** til Base**.)

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert.