Naudojant 'const' funkcijų parametrams

Kiek jūs einate su const ? Ar tik atliekate „ const funkcijas, jei reikia, ar jūs einate į visą kiaulę ir naudosite ją visur? Pavyzdžiui, įsivaizduokite paprastą mutatorių, kuriame yra vienas loginis parametras:

 void SetValue(const bool b) { my_val_ = b; } 

Ar tai tikrai const ? Asmeniškai aš norėčiau jį plačiai naudoti, įskaitant parametrus, bet šiuo atveju man įdomu, ar tai verta?

Buvau nustebęs ir sužinojęs, kad funkcijų deklaracijoje galite const parametrus iš parametrų, tačiau galite jį įtraukti į funkcijų apibrėžimą, pavyzdžiui:

.h failas

 void func(int n, long l); 

.cpp failas

 void func(const int n, const long l) 

Ar tai yra priežastis? Man atrodo šiek tiek neįprasta.

303
22 сент. nustatė Robas rugsėjo 22 d. 2008-09-22 23:11 '08 at 11:11 pm 2008-09-22 23:11
@ 30 atsakymų

Priežastis yra ta, kad parametrų parametras taikomas tik lokaliai funkcijos viduje, nes jis veikia su duomenų kopija. Tai reiškia, kad funkcijos parašas iš tikrųjų yra tas pats. Tai tikriausiai yra blogas stilius tai padaryti.

Aš asmeniškai nenoriu naudoti const, išskyrus nuorodas ir orientacinius parametrus. Nukopijuotiems objektams tai nereiškia daug, nors tai gali būti saugesnė, nes ji signalizuoja apie funkciją. Tai tikrai iššūkis. Paprastai aš naudoju const_iterator, nors kai esate pakabintas ant kažko ir aš neketinu jo keisti, todėl manau, kad kiekvienas yra jo paties, jei griežtai laikomasi nuorodų tipų teisingumo.

145
22 сент. Atsakymas, kurį pateikė Greg Rogers Sep 22 2008-09-22 23:15 '08 at 23:15 pm 2008-09-22 23:15

"const yra beprasmis, kai argumentas perduodamas pagal vertę, nes jūs nekeičiate skambinančio objekto objekto."

Klaidingas.

Tai yra jūsų kodo ir jūsų prielaidų savęs dokumentavimas.

Jei jūsų kode yra daug žmonių, kurie dirba su juo, o jūsų funkcijos yra netradicinės, jūs turėtumėte pažymėti "const" viską ir viską, ką galite. Rašydami pramonės stiprumo kodą, visada turėtumėte daryti prielaidą, kad jūsų kolegos yra psichopatai, stengdamiesi jums bet kokiu būdu (ypač dėl to, kad jie dažnai ateityje).

Be to, kaip minėta anksčiau, tai gali padėti kompiliatoriui šiek tiek optimizuoti dalykus (nors tai yra ilgas kadras).

343
22 сент. atsakymas pateikiamas rlerallut rugsėjo 22 d 2008-09-22 23:51 '08 at 11:51 pm 2008-09-22 23:51

Kartais (per dažnai!) Turiu atskleisti kito C ++ kodą. Ir mes visi žinome, kad kažkas C + + kodas yra visiškai netvarka. Taigi, pirmas dalykas, kurį noriu iššifruoti vietinį duomenų srautą, kiekvienas kintamojo apibrėžimas yra pastatytas iki kompiliatoriaus paleidimo žievė Tai taip pat reiškia konstatavimo reikšmės argumentus, nes tai tik išgalvoti vietiniai kintamieji, inicijuoti skambintojo.

Ir norėčiau, kad kintamieji, kurie yra nuolatiniai ir keičiantys, būtų reikalingi ne nuolatiniams kintamiesiems :)

121
23 сент. atsakymą pateikė Constantin 23 rugsėjis 2008-09-23 01:00 '08, 1:00, 2008-09-23 01:00

Šios dvi eilutės yra funkciniu požiūriu lygiavertės:

 int foo (int a); int foo (const int a); 

Akivaizdu, kad jūs negalėsite pakeisti foo kūno, jei jis apibrėžė antrąjį metodą, tačiau nėra skirtumo nuo šono

Kur const tikrai naudinga su nuorodų ar rodyklių parametrais:

 int foo (const BigStruct  int foo (const BigStruct *a); 

Tai rodo, kad foo gali turėti didelį parametrą, galbūt duomenų struktūrą, kurios dydis yra lygus gigabaitams, be kopijavimo. Be to, jis sako skambinančiajam: „Foo nepakeis šio parametro turinio“. Nuolatinės nuorodos perdavimas taip pat leidžia kompiliatoriui priimti tam tikrus sprendimus dėl veiklos.

*: Jei jis nepanaudoja pastovaus, bet kito įrašo.

69
22 сент. Ben Straub atsakymas, pateiktas rugsėjo 22 d 2008-09-22 23:36 '08 at 23:36 pm 2008-09-22 23:36

Papildomos nereikalingos konstantos yra blogos API požiūriu:

Perkeliant perteklinį „const“ kodą į įmontuotus tipo parametrus, kurie perduodami pagal vertę, susilpnėja jūsų API, nesuteikiant reikšmingų pažadų naudotojui ar API naudotojui (tai tik trukdo įgyvendinti).

Pernelyg daug „API“, kai tai nėra būtina, pavyzdžiui, vilkas vilkas , galiausiai žmonės pradės ignoruoti const, nes jie yra visur ir daugeliu atvejų nereiškia nieko.

Sumažinimo ad absurdumo argumentas dėl papildomų konstantų API yra tinkamas šiems dviem pirmiesiems taškams: jei yra pastovesnių parametrų, tada kiekvienas argumentas, galintis turėti konst. Tiesą sakant, jei tai būtų tikrai gera, norėtumėte, kad parametrų parametras būtų numatytasis parametras ir kad raktinis žodis, pavyzdžiui, „keičiantis“, būtų tik tada, jei norite pakeisti parametrą.

Taigi pabandykime įterpti const, jei galime:

 void mungerum(char * buffer, const char * mask, int count); void mungerum(char * const buffer, const char * const mask, const int count); 

Apsvarstykite aukščiau pateiktą kodo eilutę. Deklaracija yra ne tik užteršta, bet ir ilgesnė ir sunkiau skaitoma, tačiau API vartotojas gali saugiai ignoruoti tris iš keturių „const“ raktinių žodžių. Tačiau papildomas „const“ naudojimas padarė antrą eilutę potencialiai pavojinga!

Kodėl

Greitas neteisingas pirmojo char * const buffer parametro supratimas gali manyti, kad jis nepakeis perduodamo duomenų buferio atminties, tačiau tai nėra tiesa! Pernelyg didelis „const“ gali sukelti pavojingas ir neteisingas prielaidas apie jūsų API, kai atliekamas greitas skenavimas ar netinkamas naudojimas.


Pernelyg pastovus yra blogas ir kodų diegimo požiūriu:

 #if FLEXIBLE_IMPLEMENTATION #define SUPERFLUOUS_CONST #else #define SUPERFLUOUS_CONST const #endif void bytecopy(char * SUPERFLUOUS_CONST dest, const char *source, SUPERFLUOUS_CONST int count); 

Jei FLEXIBLE_IMPLEMENTATION yra neteisingas, tada API „žada“ neįgyvendinti šios funkcijos pirmame žemiau pateiktame kelyje.

 void bytecopy(char * SUPERFLUOUS_CONST dest, const char *source, SUPERFLUOUS_CONST int count) { // Will break if !FLEXIBLE_IMPLEMENTATION while(count--) { *dest++=*source++; } } void bytecopy(char * SUPERFLUOUS_CONST dest, const char *source, SUPERFLUOUS_CONST int count) { for(int i=0;i<count;i++) { dest[i]=source[i]; } } 

Tai labai kvailas pažadas. Kodėl turėtumėte pažadėti, kad jūsų abonentas nesuteikia jokios naudos ir apribos jūsų įgyvendinimą?

Abi šios yra visiškai teisingos tos pačios funkcijos realizacijos, nors viskas, ką darėte, yra be reikalo susieta su viena ranka už nugaros.

Be to, tai labai nedidelis pažadas, kuris yra lengvas (ir teisiškai apeinamas).

 inline void bytecopyWrapped(char * dest, const char *source, int count) { while(count--) { *dest++=*source++; } } void bytecopy(char * SUPERFLUOUS_CONST dest, const char *source,SUPERFLUOUS_CONST int count) { bytecopyWrapped(dest, source, count); } 

Klausykitės, aš tai įgyvendinau, nors pažadėjau to nedaryti - tiesiog naudokite pakavimo funkciją. Tai, kai blogas pažadas vaikinas nežudo kieno filme ir įsako jo kūnui juos nužudyti.

Šios papildomos konstantos kainuoja ne daugiau kaip tik blogo vaikino pažadas.


Tačiau gebėjimas gulėti blogėja:

Buvau nušvitęs, kad galėtumėte nesuderinti const antraštėje (deklaracija) ir kode (apibrėžimas), naudojant klaidingą const. Užuojautos teisininkai teigia, kad tai gerai, nes leidžia nustatyti tik apibrėžimą.

 // Example of const only in definition, not declaration class foo { void test(int *pi); }; void foo::test(int * const pi) { } 

Tačiau tiesa, kad priešingai - galite pateikti melagingą konstanta tik deklaracijoje ir ignoruoti ją apibrėžime. Dėl šios priežasties pernelyg sudėtinga ir siaubinga melas yra API, žr. Šį pavyzdį:

 class foo { void test(int * const pi); }; void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here { pi++; // I promised in my definition I wouldn't modify this } 

Visi pertekliniai const tikrai daro kodą mažiau suprantamą, verčia jį naudoti kitą vietinę kopijavimo ar įvyniojimo funkciją, kai nori keisti kintamąjį arba perduoti kintamąjį su ne nuolatine nuoroda.

Pažvelkite į šį pavyzdį. Kas yra lengviau skaitoma? Ar akivaizdu, kad vienintelė papildomos kintamojo priežastis antroje funkcijoje yra ta, kad kai kurie API dizaineriai išmetė papildomą pastovumą?

 struct llist { llist * next; }; void walkllist(llist *plist) { llist *pnext; while(plist) { pnext=plist->next; walk(plist); plist=pnext; // This line wouldn't compile if plist was const } } void walkllist(llist * SUPERFLUOUS_CONST plist) { llist * pnotconst=plist; llist *pnext; while(pnotconst) { pnext=pnotconst->next; walk(pnotconst); pnotconst=pnext; } } 

Tikimės, kad kažką išmokome. Pernelyg pastovus yra API srities eskizas, erzinantis nagas, smulkus ir beprasmis pažadas, nereikalinga kliūtis ir kartais sukelia labai pavojingas klaidas.

58
14 июня '12 в 18:26 2012-06-14 18:26 atsakymą Adisakas davė birželio 14 d. 12 d. 18:26 val. 2012-06-14 18:26

const turėjo būti numatytasis C ++. Čia jis yra:

 int i = 5 ; // i is a constant var int i = 5 ; // i is a real variable 
34
23 сент. atsakymas pateikiamas QBziZ rugsėjo 23 d 2008-09-23 14:43 '08 14:43 pm 2008-09-23 14:43

Kai aš užkodavau C + + gyvenimą, pasakiau viską, ką galėjau. Naudojant const yra puikus būdas padėti kompiliatoriui padėti. Pvz., Pastovaus metodo grąžinimo vertė gali jums sutaupyti klaidų, pavyzdžiui:

 foo() = 42 

kai galvojate:

 foo() == 42 

Jei foo () yra apibrėžta, kad grąžintų nekintamą nuorodą:

 int foo() {  } 

Kompiliatorius mielai leis jums priskirti reikšmę anonimiškam laikui, kurį grąžina funkcijų skambutis. Kurti jį const:

 const int foo() {  } 

Pašalina šią galimybę.

25
23 сент. Avdi atsakymas, pateiktas rugsėjo 23 d 2008-09-23 01:07 '08, 1:07 2008-09-23 01:07

Šią temą gerai aptariame senuosiuose straipsniuose „Savaitės guru“ komp.>čia .

Atitinkamas GOTW straipsnis yra prieinamas Herb Sutter svetainėje.

13
05 мая '09 в 1:21 2009-05-05 01:21 atsakymas pateikiamas Void 05 May '09 at 1:21 2009-05-05 01:21

Jei naudojate ->* arba .* Operatorius, tai būtina.

Tai neleidžia jums rašyti kažko panašaus

 void foo(Bar *p) { if (++p->*member > 0) { ... } } 

kurį aš beveik padariau dabar ir kurie tikriausiai nedaro to, ką ketinate.

Aš norėjau tai pasakyti

 void foo(Bar *p) { if (++(p->*member) > 0) { ... } } 

ir jei aš įdėti const tarp Bar * ir p , kompiliatorius man tai pasakys.

7
24 июля '12 в 3:45 2012-07-24 03:45 atsakymą įteikė Mehrdadas liepos 12 d., 12 val

Sakau const savo parametrų reikšmes.

Apsvarstykite šią funkciją:

 bool isZero(int number) { if (number = 0) // whoops, should be number == 0 return true; else return false; } 

Jei skaičiaus parametras buvo const, kompiliatorius sustabdys ir įspės apie klaidą.

7
14 нояб. Atsakymas pateikiamas Ola Ost 14 lapkričio. 2008-11-14 16:38 '08 at 4:38 pm 2008-11-14 16:38

Naudoju const parametrus funkcijų parametrams, kurie yra nuorodos (arba rodyklės), kurios yra tik [in] duomenys ir kurių funkcija nepakeis. Reikšmė, kai nuorodos naudojimo tikslas yra išvengti duomenų kopijavimo ir užkirsti kelią perduoto parametro pakeitimams.

Sąrašo const įdėjimas į būlio parametrą jūsų pavyzdyje tik apriboja įgyvendinimą ir neprisideda prie klasės sąsajos (nors dažniausiai nerekomenduojama keisti parametrų).

Parašo funkcijos

 void foo(int a); 

ir

 void foo(const int a); 

tas pats, kas paaiškina jūsų .c ir .h

Asaph

7
22 сент. Atsakymas duotas Asaf R 22 Sep. 2008-09-22 23:15 '08 at 23:15 pm 2008-09-22 23:15

Ah, sunku. Viena vertus, deklaracija yra sutartis, ir iš tikrųjų nėra prasmės perduoti argumentą const pagal vertę. Kita vertus, jei žiūrite į funkcijų įgyvendinimą, jūs suteikiate kompiliatoriui daugiau galimybių optimizuoti, jei deklaruojate nuolatinį argumentą.

5
22 сент. Atsakymą pateikė Nemanja Trifunovic rugsėjo 22 d 2008-09-22 23:18 '08 at 11:18 pm 2008-09-22 23:18

„Konst“ ženklo vertės parametrai tikrai yra subjektyvūs.

Tačiau aš tikrai norėčiau pažymėti parametrų const reikšmes, kaip ir jūsų pavyzdyje.

 void func(const int n, const long l) {  } 

Vertė man aiškiai rodo, kad parametrų funkcinės vertės niekada nekeičia funkcijos. Jie turės tą pačią reikšmę pradžioje kaip ir pabaigoje. Man tai yra labai funkcionalaus programavimo stiliaus dalis.

Trumpai tariant, tai tikriausiai yra laiko / erdvės švaistymas, kad čia būtų „const“, nes paprastai yra gana akivaizdu, kad funkcija nepakeičia argumentų.

Tačiau didesnės funkcijos atveju tai yra įgyvendinimo dokumentacijos forma ir ją vykdo kompiliatorius.

Galiu būti tikras, kad jei atliksiu kai kuriuos skaičiavimus su „n“ ir „l“, galiu pertvarkyti / perkelti šį skaičiavimą, nebijodamas gauti kitokio rezultato, nes praleidau vietą, kurioje buvo pakeistas vienas ar abu.

Kadangi tai yra įgyvendinimo detalė, jums nereikia deklaruoti galinių parametrų reikšmių antraštėje, taip pat, kaip nereikia deklaruoti funkcijos parametrų su tais pačiais pavadinimais, kaip ir įgyvendinant.

4
23 сент. Atsakymas pateikiamas Lloyd Sep 23 2008-09-23 01:58 '08 1:58 am 2008-09-23 01:58

const yra nenaudingas, kai argumentas perduodamas pagal vertę, nes jūs nekeisite skambintojo objekto.

Jei pageidaujama, kad funkcija būtų pakeista perduotą vertę, turėtų būti teikiama pirmenybė.

Galiausiai, funkcija, kuri nekeičia dabartinio objekto (tai), gali ir tikriausiai turėtų būti paskelbta const. Toliau pateikiamas pavyzdys:

 int SomeClass::GetValue() const {return m_internalValue;} 

Šis pažadas nekeičia objekto, kuriam taikomas šis kvietimas. Kitaip tariant, galite skambinti:

 const SomeClass* pSomeClass; pSomeClass->GetValue(); 

Jei funkcija nebuvo const, tai sukels įspėjimą apie kompiliatorių.

4
22 сент. Atsakyti Dan Hewett Sep 22 2008-09-22 23:40 '08 at 11:40 2008-09-22 23:40

Parametrui perduodamam parametrams nenaudoju const. Skambinančiam asmeniui nėra svarbu, ar pakeisite parametrą, ar ne, tai yra įgyvendinimo detalė.

Tiesą sakant, svarbu pažymėti metodus kaip const, jei jie nekeičia jų pavyzdžio. Padarykite tai, kai jūs einate, nes priešingu atveju galite gauti daug const_cast <>, arba galite pastebėti, kad naudojant „const“ metodą reikia pakeisti daug kodo, nes jis vadina kitus metodus, kurie turėjo būti pažymėti kaip const.

Taip pat linkiu pažymėti vietinius vars const, jei jų nereikia keisti. Manau, kad tai palengvina kodo supratimą, kad būtų lengviau nustatyti judančias dalis.

2
22 сент. Atsakymą pateikė Aurélien Gâteau . 2008-09-22 23:24 '08 at 23:24 pm 2008-09-22 23:24

Aš galiu naudoti const. Parametrų konstanta reiškia, kad jie neturėtų keisti jų vertės. Tai ypač svarbu, kai pereinama prie nuorodos. funkcija funkcijai deklaruoja, kad funkcija neturėtų keisti klasių narių.

2
22 сент. Dror Helper atsakymas rugsėjo 22 d 2008-09-22 23:22 '08 at 11:22 2008-09-22 23:22

Kai tik įmanoma, bandau naudoti const. (Arba kitas tinkamas raktinių žodžių raktinių žodžių raktinis žodis.) Tai darau tik todėl, kad leidžia kompiliatoriui atlikti papildomų optimizacijų, kurių ji negali daryti kitaip. Kadangi aš nežinau, kas yra optimizavimas, aš visada tai darau, net jei tai atrodo kvaila.

Kiek aš žinau, kompiliatorius gali labai gerai matyti const reikšmės parametrą ir sakyti: „Ei, ši funkcija nepakeičia, todėl galiu sekti nuorodą ir išsaugoti kai kurias erkes“. Nemanau, kad tai kada nors bus padaryta, nes ji keičia funkcijų parašą, bet tai yra prasminga. Galbūt jis atlieka tam tikrą manipuliavimą su kaminu ar kažką panašaus ... Viskas yra, aš nežinau, bet aš žinau, kad aš stengiuosi būti protingesni nei kompiliatorius, tai veda tik į gėdą.

„C ++“ yra papildomas bagažas, turintis omenyje pastovumą, todėl jis tampa dar svarbesnis.

2
22 сент. atsakymas pateikiamas jdmichal rugsėjo 22 d 2008-09-22 23:36 '08 at 23:36 pm 2008-09-22 23:36

Galbūt tai nebus teisingas argumentas. bet jei mes padidiname „const“ kintamojo reikšmę funkcijų kompiliatoriaus viduje, gausime klaidą: „ klaida: pridedant parametrą tik skaityti “. taigi tai reiškia, kad galime naudoti „const“ raktinį žodį kaip būdą, kaip išvengti atsitiktinių mūsų kintamųjų pakeitimų funkcijose (kurių neturėtume naudoti / skaityti tik). todėl, jei atsitiktinai tai padarėme kompiliavimo metu, kompiliatorius apie tai praneš. Tai ypač svarbu, jei nesate vienintelis, dirbantis su šiuo projektu.

2
04 апр. Atsakymas, kurį pateikė HarshaXsoad 04 Bal 2015-04-04 00:21 '15 prie 0:21 2015-04-04 00:21

Tokiu atveju, kai paminėtumėte, tai neturi įtakos jūsų API skambinančiajam naudotojui, todėl paprastai tai nėra padaryta (ir to nereikia antraštėje). Tai turi įtakos tik jūsų funkcijos įgyvendinimui.

Tai nėra ypač bloga, tačiau nauda nėra tokia didelė, nes jie neturi įtakos jūsų API ir prideda rašymo būdą, todėl paprastai tai nėra padaryta.

2
22 сент. Luke Halliwell atsakymas rugsėjo 22 d 2008-09-22 23:17 '08 at 23:17 pm 2008-09-22 23:17

Apie kompiliatoriaus optimizavimą: http://www.gotw.ca/gotw/081.htm

1
06 мая '09 в 0:53 2009-05-06 00:53 atsakymas pateikiamas sdcvvc 06 d., 09 val. 0:53 2009-05-06 00:53

Jei parametras yra perduodamas pagal vertę (ir nėra nuoroda), paprastai yra mažai skirtumo, ar parametras yra deklaruotas kaip const, ar ne (jei jame nėra atskaitos elemento - jokių problemų, susijusių su integruotais tipais). Jei parametras yra nuoroda ar rodyklė, paprastai geriau apsaugoti nuorodą / nukreiptą atmintį, o ne pačią rodyklę (manau, kad negalite susieti su pats „const“, o ne todėl, kad jis yra svarbus, nes negalite pakeisti teisėjo), atrodo Tai gera idėja apsaugoti viską, ką galite, kaip const. Jį galite praleisti be baimės padaryti klaidą, jei parametrai yra tik POD (įskaitant įmontuotus tipus), ir nėra jokios galimybės, kad jie pasikeis toliau keliu (pvz., Jūsų pavyzdyje parametras „Bool“).

Aš nežinojau, kaip atskirti .h / .cpp failo deklaracijas, bet tai yra prasminga. Mašinos kodo lygiu nieko nėra „const“, todėl, jei deklaruojate funkciją (.h) kaip ne const, kodas yra toks pat, kaip jei jį paskelbėte kaip const (pusės optimizavimas). Tačiau tai padės jums prijungti kompiliatorių, kad nekeisite kintamojo vertės funkcijų įgyvendinime (.ccp). Tai gali būti naudinga, kai paveldėsite sąsają, kuri leidžia jums keisti, bet jums nereikia keisti parametro norint pasiekti norimą funkciją.

1
22 сент. Atsakymą pateikė Attila Sep 22 2008-09-22 23:27 '08, 23:27 pm 2008-09-22 23:27

Pastovus parametras yra naudingas tik tada, kai parametras yra perduodamas pagal nuorodą, ty ir nuorodą, ir rodyklę. Kai kompiliatorius mato const parametrą, jis turi užtikrinti, kad parametre naudojamas kintamasis nepakeistų funkcijų korpuse. Kodėl kas nors nori, kad šalutinės vertės parametras būtų pastovus?: -)

1
22 сент. atsakymą pateikė Jogų Baliga 22 sep . 2008-09-22 23:27 '08, 23:27 pm 2008-09-22 23:27

Apibendrinant:

  • „Paprastai const-by-value const yra nepatikimas ir geriausia klaidinantis“. Iš GOTW006
  • Bet jūs galite juos įtraukti į .cpp ir kintamuosius.
  • Atminkite, kad standartinė biblioteka nenaudoja const. Pavyzdžiui. std::vector::at(size_type pos) . Kas tinka standartinei bibliotekai, yra gera man.
1
26 июля '17 в 12:36 2017-07-26 12:36 atsakymą pateikė „ YvesgereY “ liepos 26 d. 17, 12:36 2017-07-26 12:36

Tokiems parametrams nenustatysiu konstantų - visi jau žino, kad loginis (o ne Būlio ir amp;) yra pastovus, todėl pridedant jį bus žmonės galvoja „laukti, ką?“. arba netgi jūs perduodate parametrą pagal nuorodą.

0
22 сент. Atsakymą pateikė Khoth Sep 22 2008-09-22 23:17 '08 at 23:17 pm 2008-09-22 23:17

Kadangi parametrai perduodami pagal vertę, nesvarbu, ar nurodote const, ar ne, kalbant apie skambinimo funkciją. Iš esmės nėra prasmės deklaruoti vertės parametrų kaip konst.

0
ответ дан siddharth 23 сент. '08 в 14:37 2008-09-23 14:37