Fonksiyon parametreleri için 'const' kullanımı


397

Ne kadar uzağa gidiyorsun const? Sadece constgerektiğinde işlevler mi yapıyorsunuz yoksa tüm domuzu alıp her yerde mi kullanıyorsunuz? Örneğin, tek bir boolean parametresi alan basit bir mutator düşünün:

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

Bu constgerçekten faydalı mı? Şahsen, parametreler de dahil olmak üzere yaygın olarak kullanmayı tercih ediyorum, ancak bu durumda bunun değerli olup olmadığını merak ediyorum?

constBir işlev bildirimindeki parametrelerden atlayabileceğinizi , ancak işlev tanımına dahil edebileceğinizi öğrenmek için şaşırdım , örneğin:

.h dosyası

void func(int n, long l);

.cpp dosyası

void func(const int n, const long l)

Bunun bir sebebi var mı? Bana biraz alışılmadık geliyor.


Katılmıyorum. .H dosyası da const tanımlarına sahip olmalıdır. Değilse, const parametreleri işleve iletilirse, .h dosyasındaki prototip const tanımlarına sahip olmadığından derleyici bir hata oluşturur.
selwyn

10
Katılıyorum. :-) (Soru ile, son yorum değil!) Fonksiyonun gövdesinde bir değer değiştirilmemesi gerekiyorsa, bu aptalca == veya = hataları durdurmaya yardımcı olabilir, asla ikisine de const koymamalısınız, değerle geçti, aksi takdirde yapmanız gerekir) Bununla ilgili tartışmalara girecek kadar ciddi değil!
Chris Huang-Leaver

19
@selwyn: İşleve const int iletseniz bile, kopyalanacaktır (bu bir referans olmadığı için) ve dolayısıyla sabitliğin önemi yoktur.
jalf

1
Bu soruda da aynı tartışmalar yaşanıyor: stackoverflow.com/questions/1554750/…
Kısmi

5
Bu yazının birkaç yaşında olduğunu anlıyorum, ancak yeni bir programcı olarak bu soruyu merak ediyordum ve bu konuşmaya tökezledim. Bence, bir işlev bir değeri değiştirmemeli, bu bir başvuru veya değerin / nesnenin bir kopyası olsun, const olmalıdır. Daha güvenlidir, kendi kendini belgelendirir ve daha hata ayıklama dostudur. Bir ifadesi olan en basit işlev için bile, hala const kullanıyorum.

Yanıtlar:


187

Bunun nedeni, verilerin bir kopyası üzerinde çalıştığı için, parametrenin sabitinin yalnızca işlev içinde yerel olarak geçerli olmasıdır. Bu, işlev imzasının gerçekten de aynı olduğu anlamına gelir. Yine de bunu çok yapmak muhtemelen kötü bir stil.

Ben şahsen referans ve işaretçi parametreleri dışında const kullanma eğilimindedir. Kopyalanan nesneler için bu gerçekten önemli değildir, ancak işlev içinde niyet sinyali verdiğinden daha güvenli olabilir. Bu gerçekten bir karar çağrısı. Ben bir şey üzerinde döngü yaparken const_iterator kullanmaya eğilimindedir ve ben bunu değiştirmek niyetinde değilim, bu yüzden referans türleri için const doğruluğu titizlikle korunur sürece her biri kendi sanırım.


57
'Kötü stil' kısmına katılamıyorum. Damlama constfonksiyon prototipleri gelen yorulana karar verirseniz başlık dosyasını değiştirmek gerekmez avantajına sahiptir constsonradan uygulama bölümünden.
Michał Górny

4
"Ben şahsen referans ve işaretçi parametreleri dışında const kullanma eğilimindedir." Belki de "işlev bildirimlerinde gereksiz niteleyicileri kullanma eğiliminde değilim, constyararlı bir fark yaratan yerlerde kullanma eğiliminde olduğumu" açıklığa kavuşturmalısınız .
Tekilleştirici

3
Bu cevaba katılmıyorum. Diğer yöne yaslanıyorum ve constmümkün olduğunda parametreleri işaretliyorum ; daha etkileyici. Başkasının kodunu okuduğumda, büyü numaraları, yorum yapma ve doğru işaretçi kullanımı gibi şeylerin yanı sıra kodlarını yazmaya ne kadar özen gösterdiklerini yargılamak için bunun gibi küçük göstergeler kullanıyorum
Ultimater

4
int getDouble(int a){ ++a; return 2*a; }Bunu dene. Tabii ki, ++aorada ilgisi yok ama olabilir uzun bir süre boyunca birden fazla programcı tarafından yazılmış uzun bir işlevde orada bulunabilir. int getDouble( const int a ){ //... }Bulurken derleme hatası üretecek yazmak şiddetle tavsiye ediyorum ++a;.
dom_beau

3
Her şey kimin hangi bilgiye ihtiyacı olduğu meselesidir. Parametreyi değere göre sağlarsınız , böylece arayanın onunla (dahili olarak) ne yaptığınız hakkında hiçbir şey bilmesine gerek kalmaz . Bu yüzden class Foo { int multiply(int a, int b) const; }başlığınıza yazın. Uygulamanızda, değiştirmemeye söz verebileceğinize önem veriyorsunuz ave bbu yüzden int Foo::multiply(const int a, const int b) const { }burada mantıklı. (Sidenote: hem arayan hem de uygulama, işlevin Foonesnesini değiştirmediği gerçeğini önemsiyor , bu nedenle beyanının sonunda sabit)
CharonX

415

msgstr "arayanın nesnesini değiştirmeyeceğiniz için argüman değere göre iletildiğinde const anlamsız."

Yanlış.

Bu, kodunuzu ve varsayımlarınızı kendiniz belgelemeyle ilgilidir.

Kodunuz üzerinde çalışan birçok kişi varsa ve işlevleriniz önemsiz değilse, "const" i işaretleyebilirsiniz. Endüstriyel güç kodunu yazarken, her zaman iş arkadaşlarınızın sizi mümkün olan her şekilde elde etmeye çalışan psikopatlar olduğunu varsaymalısınız (özellikle gelecekte genellikle kendiniz olduğundan).

Ayrıca, daha önce de belirtildiği gibi, derleyicinin işleri biraz optimize etmesine yardımcı olabilir (uzun bir çekim olmasına rağmen).


41
Tamamen katılıyorum. Her şey insanlarla iletişim kurmak ve bir değişkenle neler yapılabileceğini kısıtlamak.
Len Holgate

19
Buna oy verdim. Bence değer bağımsız değişkenlerine basit geçişe uyguladığınızda const ile göstermeye çalıştığınız şeyi sulandırıyorsunuz.
tonylo

26
Buna oy verdim. 'Const' parametresinin bildirilmesi, parametreye anlamsal bilgi ekler. Kodun orijinal yazarının neyi amaçladığını vurgularlar ve bu, zaman geçtikçe kodun korunmasına yardımcı olur.
Richard Corden

13
@tonylo: yanlış anladın. Bu, yerel bir değişkeni bir kod bloğu içinde const olarak işaretlemekle ilgilidir (bu bir işlevdir). Aynı şeyi herhangi bir yerel değişken için de yapardım. Gerçekten de önemli olan, doğru olan bir API'ye sahip olmak diktir.
rlerallut

56
Ve işlevin içindeki hataları yakalayabilir - bir parametrenin değiştirilmemesi gerektiğini biliyorsanız, const'un bildirilmesi derleyicinin yanlışlıkla değiştirip değiştirmediğinizi size söyleyeceği anlamına gelir.
Adrian

156

Bazen (çok sık!) Başkasının C ++ kodunu çözmek zorundayım. Ve hepimiz bir başkasının C ++ kodunun neredeyse tanım gereği tam bir karmaşa olduğunu biliyoruz :) Yani yerel veri akışını deşifre etmek için yaptığım ilk şey derleyici havlayana kadar her değişken tanımında const koymak . Bu da const niteleyici değer argümanları anlamına gelir, çünkü bunlar sadece arayan tarafından başlatılan süslü yerel değişkenlerdir.

Ah, keşke değişkenler varsayılan olarak sabit olsaydı ve sabit olmayan değişkenler için değiştirilebilir gerekli :)


4
"Keşke değişkenler varsayılan olarak const" - bir oksimoron ?? 8-) Cidden, her şeyi "sorgulamak" kodu çözmenize nasıl yardımcı olur? Orijinal yazar sözde sabit bir argümanı değiştirdiyse, varlığın sabit olması gerektiğini nereden biliyorsunuz? Dahası, (bağımsız değişken) değişkenlerin büyük çoğunluğu ... değişkenler içindir. Yani derleyici sürece başladıktan hemen sonra kırılmalı, değil mi?
ysap

8
@ysap, 1. Sabitin olabildiğince işaretlenmesi hangi parçaların hareket ettiğini, hangilerinin hareket etmediğini görmemi sağlar. Benim tecrübeme göre, pek çok yerli de facto const, tersi değil. 2. "Sabit değişken" / "Değişmez değişken" oksimoron olarak ses çıkarabilir, ancak fonksiyonel dillerin yanı sıra işlevsel olmayan bazı diller için de standart bir uygulamadır; Rust'a
Constantin

1
Ayrıca standart şimdi bazı durumlarda c ++; örneğin, lambda [x](){return ++x;}bir hatadır; buraya
anatolyg

10
Değişkenler "dir constvarsayılan olarak" Rust :)
anka

Değişkenlerin değişebilmeleri için atanabilir olmaları gerekmez; başlatıldıkları değer çalışma zamanında da değişebilir.
Sphynx

80

Aşağıdaki iki çizgi işlevsel olarak eşdeğerdir:

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

Açıkçası , ikinci şekilde tanımlanmışsa, avücutta değişiklik yapamazsınız foo, ancak dışarıdan bir fark yoktur.

Nerede const gerçekten referans veya işaretçi parametreleri ile kullanışlı geliyor:

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

Bunun söylediği şey, foo'nun büyük bir parametre, belki de gigabayt boyutundaki bir veri yapısını kopyalamaksızın alabilmesi. Ayrıca, arayan kişiye "Foo * bu parametrenin içeriğini değiştirmeyecektir" der. Bir const referansı geçmek, derleyicinin belirli performans kararları vermesine de izin verir.

*: Sabitliği bozmadıkça, ama bu başka bir gönderi.


3
Bu sorunun konusu bu değil; Tabii ki referanslı veya sivri uçlu argümanlar için const kullanmak iyi bir fikirdir (referans verilen veya sivri uçlu değer değiştirilmemişse). İşaretçi örneğinizde const olan parametre olmadığını unutmayın; parametrenin işaret ettiği şey budur.
tml

> Bir sabit referansı geçmek derleyicinin belirli performans kararları vermesini de sağlar. klasik yanlışlık - derleyici kendi kendini belirlemek zorundadır, const anahtar işaretçi takma ve const_cast sayesinde bu konuda yardımcı olmaz
jheriko

73

Ekstra Gereksiz const, bir API açısından kötüdür:

Değerden geçirilen gerçek tip parametreler için kodunuza fazladan gereksiz const koymak, API'nızı arayarak API veya API kullanıcısına anlamlı bir söz vermez (yalnızca uygulamayı engeller).

Bir API'de ihtiyaç duyulmadığında çok fazla 'const' " ağlayan kurt " gibidir, sonunda insanlar 'const'u görmezden gelmeye başlar, çünkü her yerde vardır ve çoğu zaman hiçbir şey ifade etmez.

API'da ekstra consts için "reductio ad absurdum" argümanı, bu ilk iki nokta için iyidir, daha fazla const parametresi iyi ise, o zaman bir const olabilecek her argümanın üzerinde bir const olması gerekir. Aslında, gerçekten iyi olsaydı, const'un parametreler için varsayılan olmasını ve yalnızca parametreyi değiştirmek istediğinizde "mutable" gibi bir anahtar kelimeye sahip olmasını istersiniz.

Yani, mümkün olan her yerde const koymayı deneyelim:

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

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

Yukarıdaki kod satırını düşünün. Beyan sadece daha karmaşık ve uzun ve okunması daha zor olmakla kalmaz, aynı zamanda dört 'const' anahtar kelimeden üçü API kullanıcısı tarafından güvenli bir şekilde göz ardı edilebilir. Bununla birlikte, 'const'un ekstra kullanımı ikinci satırı potansiyel olarak TEHLİKELİ hale getirdi !

Neden?

İlk parametrenin hızlı bir şekilde yanlış okunması char * const buffer, aktarılan veri arabelleğindeki belleği değiştirmeyeceğini düşünmenizi sağlayabilir - ancak bu doğru değildir! Gereksiz 'const', hızlı bir şekilde tarandığında veya yanlış okunduğunda API'nız hakkında tehlikeli ve yanlış varsayımlara yol açabilir .


Gereksiz yapı, Kod Uygulama açısından da kötüdür:

#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);

FLEXIBLE_IMPLEMENTATION doğru değilse, API işlevi ilk yoldan uygulamaya koymamak için “umut vericidir”.

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];
       }
}

Bu çok aptalca bir söz. Neden arayana hiç fayda sağlamayan ve sadece uygulamanızı sınırlandıran bir söz vermelisiniz?

Bunların her ikisi de aynı işlevin mükemmel geçerli uygulamalarıdır, ancak yaptığınız tek şey gereksiz yere bir elinizin arkasına bağlanmıştır.

Dahası, kolayca (ve yasal olarak atlatılmış) çok sığ bir vaattir.

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);
}

Bakın, sadece bir sarmalayıcı işlevini kullanmamaya söz vermiş olsam da bu şekilde uyguladım. Kötü adam bir filmdeki birini öldürmemeye söz verdiği ve onun adamına onları öldürmesini emrettiği zamanki gibi.

Bu gereksiz yapılar, kötü adam filminden bir vaatten daha değerli değildir.


Ancak yalan söyleme yeteneği daha da kötüleşir:

Ben sahte const kullanarak üstbilgi (bildirim) ve kod (tanım) const uyumsuz aydınlandı. Const-happy savunucuları bunun iyi bir şey olduğunu iddia ediyor çünkü const'u sadece tanımına koymanıza izin veriyor.

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

Ancak, tersi doğrudur ... sadece beyanda sahte bir sabit koyabilir ve tanımda görmezden gelebilirsiniz. Bu, bir API'daki gereksiz konstrüksiyonu daha korkunç bir şey ve korkunç bir yalan haline getirir - bu örneğe bakın:

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
}

Gereksiz tüm yapı, değişkeni değiştirmek veya değişkeni sabit olmayan referansla geçirmek istediğinde, başka bir yerel kopyayı veya bir sarmalayıcı işlevini kullanmaya zorlayarak uygulayıcının kodunu daha az okunabilir hale getirmektir.

Bu örneğe bakın. Hangisi daha okunabilir? İkinci fonksiyondaki ekstra değişkenin tek sebebinin, bazı API tasarımcılarının gereksiz bir yapıya atması olduğu açık mı?

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

İnşallah burada bir şeyler öğrendik. Gereksiz yapı, bir API-dağınıklık gözü, sinir bozucu bir nag, sığ ve anlamsız bir vaat, gereksiz bir engeldir ve bazen çok tehlikeli hatalara yol açar.


9
Neden inişler? Bir downvote hakkında kısa bir yorum bırakırsanız çok daha yararlı olur.
Adisak

7
Const bağımsız değişkenini kullanmanın tüm noktası, işaretli satırı başarısız yapmaktır (plist = pnext). İşlev argümanını değişmez tutmak makul bir güvenlik önlemidir. İşlev beyanlarında (bunların gereksiz olduklarından) kötü olduklarını, ancak uygulama bloğunda amaçlarına hizmet edebileceklerini kabul ediyorum.
touko

23
@Adisak Cevabınızla ilgili yanlış bir şey görmüyorum, ama yorumlarınızdan önemli bir noktayı kaçırdınız. İşlev tanımı / uygulaması , yalnızca işlev bildirimi olan API'nin bir parçası değildir . Söylediğiniz gibi, const parametreleriyle işlevleri bildirmek anlamsızdır ve karmaşa ekler. Ancak API kullanıcılarının uygulamalarını asla görmeleri gerekmeyebilir. Bu arada uygulayıcı, SADECE netlik için işlev tanımındaki bazı parametrelerin sabit kalmasına karar verebilir, ki bu gayet iyi durumdadır.
jw013

17
@ jw013 doğrudur void foo(int)ve void foo(const int)aşırı yüklenmeler değil aynı işlevlerdir. ideone.com/npN4W4 ideone.com/tZav9R Buradaki sabit, işlev gövdesinin yalnızca bir uygulama ayrıntısıdır ve aşırı yük çözünürlüğü üzerinde hiçbir etkisi yoktur. Daha güvenli ve daha düzenli bir API için const'u bildirimin dışında bırakın, ancak kopyalanan değeri değiştirmemeyi düşünüyorsanız tanıma const koyun .
Oktalist

3
@Adisak Bunun eski olduğunu biliyorum, ancak genel bir API için doğru kullanımın tersi olacağına inanıyorum. Bu şekilde, iç pi++kısımlar üzerinde çalışan geliştiriciler, ne zaman olması gerektiği gibi hata yapmazlar .
CoffeeandCode

39

const, C ++ 'da varsayılan olmalıdır. Bunun gibi :

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable

8
Tam olarak düşüncelerim.
Constantin

24
C ile uyumluluk, en azından C ++ tasarlayan insanlar için bile bunu dikkate almak için çok önemlidir.

4
İlginç, bunu hiç düşünmemiştim.
Dan

6
Benzer şekilde, unsignedC ++ 'da varsayılan olmalıdır. Bunun gibi: int i = 5; // i is unsignedve signed int i = 5; // i is signed.
hkBattousai

25

C ++ 'ı bir yaşam için kodladığımda, yapabileceğim her şeyi düşündüm. Const kullanmak, derleyicinin size yardımcı olması için harika bir yoldur. Örneğin, yöntem döndürme değerlerinizi korumak sizi aşağıdaki gibi yazım hatalarından kurtarabilir:

foo() = 42

ne demek istiyorsan:

foo() == 42

Const olmayan bir başvuru döndürmek için foo () tanımlanmışsa:

int& foo() { /* ... */ }

Derleyici, işlev çağrısı tarafından döndürülen anonim geçici dosyaya bir değer atamanıza izin verir. Sabit yapma:

const int& foo() { /* ... */ }

Bu olasılığı ortadan kaldırır.


6
Hangi derleyici ile çalıştı? GCC derlemeye çalışırken bir hata veriyor foo() = 42: hata: atamanın sol işleneni olarak
lvalue

Bu sadece yanlış. foo () = 42, 2 = 3 ile aynıdır, yani bir derleyici hatası. Ve bir const döndürmek tamamen anlamsız. Yerleşik tip için hiçbir şey yapmaz.
Josh

2
Bu const kullanımı ile karşılaştım ve size söyleyebilirim, sonunda faydalarından daha fazla güçlük üretir. İpucu: İşlev işaretçileri, sinyal / yuva sistemleri veya boost :: bind gibi şeyler kullanıyorsanız, sizi büyük sorun haline getiren const int foo()türden farklıdır int foo().
Mephane

2
Referans dönüş değerini eklemek için kodu düzelttim.
Avdi

Dönüş değeri optimizasyonu nedeniyle const int& foo()etkin olarak aynı değil int foo()mi?
Zantier

15

Burada yönetilen comp.lang.c ++ 'ın eski "Haftanın Guru" makalelerinde bu konuda iyi bir tartışma var .

İlgili GOTW makalesine Herb Sutter'in web sitesinden ulaşabilirsiniz .


1
Herb Sutter gerçekten akıllı bir adam :-) Kesinlikle okumaya değer ve onun puan TÜM katılıyorum.
Adisak

2
Güzel makale ama onunla tartışmaya katılmıyorum. Değişkenlere benzedikleri için onları dayasıyorum ve asla argümanlarımda herhangi bir değişiklik yapmasını istemiyorum.
QBziZ

9

Ben senin const değer parametrelerinizi söylüyorum.

Bu araç işlevini düşünün:

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

Number parametresi const olsaydı, derleyici durur ve bizi hata konusunda uyarırdı.


2
başka bir yol ise if (0 == sayı) ... else ...;
Johannes Schaub - litb

5
@ ChrisHuang-Leaver Korkunç değil, eğer Yoda gibi konuşursanız: stackoverflow.com/a/2430307/210916
MPelletier

GCC / Clang -Wall size -Özellikle yapmak istediğiniz şeyse “if ((sayı = 0))” yapmanızı gerektiren -Beyazlar verir. Hangi Yoda olmanın yerine çalışır.
Jetski S-tipi

8

Ben sadece [in] veri ve işlev tarafından değiştirilmez referanslar (veya işaretçiler) olan işlev parametreleri üzerinde const kullanın. Bir referans kullanmanın amacı, verilerin kopyalanmasını önlemek ve iletilen parametrenin değiştirilmesine izin vermemektir.

Örneğinizde boolean b parametresine const koymak yalnızca uygulamaya bir kısıtlama getirir ve sınıfın arabirimine katkıda bulunmaz (parametreleri değiştirmemeniz önerilir).

İçin işlev imzası

void foo(int a);

ve

void foo(const int a);

aynıdır, bu da .c ve .h'nizi açıklar

Asaf


6

Eğer kullanırsanız ->*veya.* operatörleri, bu bir zorunluluktur.

Gibi bir şey yazmanızı engeller

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

ki şu anda neredeyse yaptım ve muhtemelen niyetini yapmıyorsun.

Söylemek istediğim

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

Ben koymuştu ve eğer constaradaki Bar *ve p, derleyici bana bunu söylerdi.


4
Birçok operatörü (% 100'ü zaten bilmiyorsam) karıştırmak üzereyken hemen operatör önceliği referansını kontrol ediyorum, bu yüzden IMO bu bir sorun değil.
mk12

5

Ah, zor biri. Bir tarafta, bir açıklama bir sözleşmedir ve bir sabit argümanı değere göre geçmek gerçekten mantıklı değildir. Öte yandan, işlev uygulamasına bakarsanız, bir bağımsız değişken sabiti bildirirseniz derleyiciye en iyi duruma getirme şansı verirsiniz.


5

const, arayanın nesnesini değiştirmeyeceğiniz için bağımsız değişken değere iletildiğinde anlamsızdır.

const, işlevin amacı aktarılan değeri değiştirmek olmadığı sürece referans ile geçerken tercih edilmelidir.

Son olarak, geçerli nesneyi (bu) değiştirmeyen bir işlev sabit olabilir ve muhtemelen bildirilmelidir. Aşağıda bir örnek verilmiştir:

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

Bu, bu çağrının uygulandığı nesneyi değiştirmeme vaadidir. Başka bir deyişle, şunları arayabilirsiniz:

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

Eğer işlev sabit değilse, bu bir derleyici uyarısına neden olur.


5

Değer parametrelerini 'const' olarak işaretlemek kesinlikle öznel bir şeydir.

Ancak ben aslında örnekte olduğu gibi const değer parametrelerini işaretlemeyi tercih ederim.

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

Bana verilen değer, işlev parametre değerlerinin işlev tarafından hiçbir zaman değiştirilmediğini açıkça göstermektedir. Başlangıçta sonunda olduğu gibi aynı değere sahip olacaklar. Benim için çok işlevsel bir programlama tarzına uymanın bir parçası.

Kısa bir işlev için, argümanların işlev tarafından değiştirilmediği genellikle açıktır, çünkü orada 'const' olması muhtemelen zaman / alan kaybıdır.

Bununla birlikte, daha büyük bir işlev için, bir uygulama belgesi biçimidir ve derleyici tarafından uygulanır.

'N' ve 'l' ile bir hesaplama yaparsam, farklı bir sonuç alma korkusu olmadan bu hesaplamayı yeniden düzenleyebilir / taşıyabilirim çünkü bir veya her ikisinin de değiştiği bir yeri kaçırdım.

Bir uygulama ayrıntısı olduğundan, tıpkı uygulamada kullanılanlarla aynı adlara sahip işlev parametrelerini bildirmeniz gerekmediği gibi, başlıktaki const değer parametrelerini bildirmeniz gerekmez.


4

Bu geçerli bir argüman olmayabilir. ancak bir işlev derleyicisinin içindeki bir const değişkeninin değerini artırırsak, bize bir hata verir: " hata: salt okunur parametrenin artışı ". yani bu, const anahtar sözcüğünü, işlevler içindeki değişkenlerimizi yanlışlıkla değiştirmeyi önlemenin bir yolu olarak kullanabileceğimiz anlamına gelir. yani derleme zamanında bunu yaparsak derleyici bunu bize bildirir. bu proje üzerinde çalışan tek kişi siz değilseniz bu özellikle önemlidir.


3

Mümkün olan yerlerde const kullanma eğilimindeyim. (Ya da hedef dil için uygun başka bir anahtar kelime.) Bunu sadece derleyicinin başka türlü yapamayacağı ekstra optimizasyonlar yapmasına izin verdiği için yapıyorum. Bu optimizasyonların ne olabileceği hakkında hiçbir fikrim olmadığından, aptal göründüğü yerlerde bile her zaman yaparım.

Bildiğim kadarıyla, derleyici bir const değeri parametresini çok iyi görebilir ve "Hey, bu işlev zaten değiştirmiyor, bu yüzden referansla geçebilir ve bazı saat döngülerini kaydedebilirim" diyebilir. İşlev imzasını değiştirdiği için böyle bir şey yapacağını sanmıyorum, ama bu noktaya değiniyor. Belki farklı yığın manipülasyonu falan yapar ... Mesele şu ki, bilmiyorum, ama derleyicinin sadece utanmamıza yol açtığından daha akıllı olmaya çalışıyorum.

C ++, doğruluk fikri ile bazı ekstra bagajlara sahiptir, bu yüzden daha da önemli hale gelir.


Bazı durumlarda yardımcı olsa da, optimizasyonları teşvik etme olasılığının bir fayda olarak önemli ölçüde abartıldığından şüpheleniyorum const. Daha ziyade, uygulama içinde niyet belirtmek ve daha sonra düşünürleri yakalamak meselesidir (yanlışlıkla yanlış yerel değişkeni arttırmak, çünkü değildi const). Paralel olarak, derleyicilerin işlev imzalarını değiştirebilmeleri açısından işlev imzalarını değiştirmeye çok açık olduklarını ve satırlar çizildiğinde tüm çalışma yollarının değiştirilebileceğini de ekleyeceğim; referans ekleme veya kaldırma, 'değişken' değişmezi yapma, vb. tümü as-if kuralındadır
underscore_d

3

1. Değerlendirmeme dayalı en iyi cevap:

@Adisak'ın cevabı, değerlendirmeme dayanarak en iyi cevap. Not aynı zamanda, çünkü bu cevabı en iyi parçası olduğunu en iyi destekli gerçek kod örnekleri ile , ses ve iyi düşünülmüş mantığını kullanarak ek olarak.

2. Kendi sözlerim (en iyi yanıtı kabul ederek):

  1. By-pass değeri için katmanın bir yararı yoktur const. Tek yaptığı:
    1. uygulayıcıyı, kaynak kodundaki bir giriş parametresini her değiştirmek istediklerinde bir kopya yapmak zorunda bırakmakla sınırlandırın (geçirilen değer, zaten by-pass değeri olduğundan zaten bir kopya olduğundan, bu değişikliğin herhangi bir yan etkisi olmayacaktır). Sıklıkla, değerin iletildiği bir giriş parametresinin değiştirilmesi işlevi uygulamak için kullanılır, böylece consther yere eklemek bunu engelleyebilir.
    2. ve constkodun consther yerde gereksiz yere eklenmesi const, güvenli kod olması için gerçekten gerekli olan şeylerden dikkat çekmektir .
  2. İle uğraşırken işaretçiler veya referanslar , ancak, constgerekli ve ne zaman çok önemlidir gerekir kullanılabilir persistan değişikliklerle istenmeyen yan etkileri engeller olarak, dış fonksiyonu ve dolayısıyla her bir işaretçi veya referans gerekir kullanmak constPARM bir giriş sadece olduğunda, çıktı değil. kullanmaconst Yalnızca referans veya işaretçi tarafından geçirilen parametrelerde , hangi parametrelerin işaretçi veya referans olduğunu gerçekten belirgin hale getirmenin ek bir yararı vardır. Dışarı çıkıp "Dikkat et! constYanındaki herhangi bir parametre bir referans veya işaretçi!" Demek için bir şey daha var .
  3. Yukarıda tarif ettiğim, sıklıkla çalıştığım profesyonel yazılım kuruluşlarında sağlanan fikir birliği oldu ve en iyi uygulama olarak görülüyordu. Bazen bile, kural katıdır: "değere göre iletilen parametrelerde sabit kullanmayın, ancak yalnızca girdilerse her zaman başvuru veya işaretçi tarafından geçirilen parametrelerde kullanın."

3. Google'ın kelimeleri (benimle ve en iyi yanıtı kabul ederek):

(" Google C ++ Stil Kılavuzu " ndan)

Değere göre iletilen bir işlev parametresi için const'ın arayan üzerinde hiçbir etkisi yoktur, bu nedenle işlev bildirimlerinde önerilmez. Bkz. TotW # 109 .

Yerel değişkenler üzerinde const kullanımı ne cesaretlendirilir ne de cesaret kırılır.

Kaynak: Google C ++ Stil Kılavuzu'nun "Sabit kullanım" bölümü: https://google.github.io/styleguide/cppguide.html#Use_of_const . Bu aslında gerçekten değerli bir bölüm, bu yüzden tüm bölümü okuyun.

"TotW # 109" un "Haftanın İpucu # 109: constİşlev Beyanlarında Anlamlı " anlamına geldiğini ve aynı zamanda yararlı bir okuma olduğunu unutmayın. Daha bilgilendirici ve yapılması gerekenler konusunda daha az kuralcı ve geldi bağlamına dayalı olduğu daha önce Google C ++ Stil Kılavuzu kuralconst yukarıda alıntılanan , ancak sağladığı netliğin bir sonucu olarak, constyukarıda alıntılanan kural Google C ++ 'a eklendi Stil rehberi.

Ayrıca, konumumu savunmak için burada Google C ++ Stil Kılavuzu'ndan alıntı yapmama rağmen, her zaman kılavuzu takip ettiğim veya her zaman kılavuzu takip etmeyi önermediğim anlamına gelmez. Tavsiye ettikleri şeylerden bazıları, "Sabit İsimler" için kendi kDaysInAWeekstil adlandırma kuralları gibi, sadece garip . Bununla birlikte, dünyanın en başarılı ve etkili teknik ve yazılım şirketlerinden birinin ben ve @Adisak gibi diğerleri ile aynı gerekçeyi ne zaman kullandığını belirtmek yine de yararlı ve önemlidir.

4. Clang'ın linter'i clang-tidy, bunun için bazı seçeneklere sahiptir:

C. Clang en linter, dikkati çekiyor da var clang-tidy, bir seçenek vardır readability-avoid-const-params-in-decls, burada açıklanan destekleme, bir kod tabanı zorlama değil kullanılarak constpass-by-value fonksiyon parametreleri için :

İşlev bildiriminin en üst düzey sabit olan parametrelere sahip olup olmadığını denetler.

beyanlardaki const değerleri bir fonksiyonun imzasını etkilemez, bu nedenle oraya konulmamalıdır.

Örnekler:

void f(const string);   // Bad: const is top level.
void f(const string&);  // Good: const is not top level.

İşte tamlık ve netlik için kendime eklediğim iki örnek daha:

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]

B. Ayrıca bu seçeneği vardır: readability-const-return-type- https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

5. Konuyla ilgili bir stil rehberini nasıl söyleyeceğime pragmatik yaklaşımım:

Bunu kopyalayıp stil kılavuzuma yapıştırırım:

[KOPYA / MACUN BAŞLANGIÇ]

  1. constİçeriklerinin (işaret ettikleri şey) DEĞİŞTİRİLMEMESİ amaçlandığında daima referans veya işaretçi ile iletilen fonksiyon parametrelerini kullanın . Bu şekilde, referans veya işaretçi IS tarafından iletilen bir değişkenin ne zaman değiştirilmesi beklendiği açıktır, çünkü eksik olacaktır const. Bu kullanım durumunda const, işlevin dışındaki yan etkileri önler.
  2. O edilir önerilmez kullanımına constçünkü değer geçirilecek fonksiyon parametreleri üzerine constarayanın üzerinde bir etkisi yoktur: değişken işlevinde değişse bile işlevi dışında hiçbir yan etkisi olmayacaktır. Ek gerekçe ve öngörü için aşağıdaki kaynaklara bakın:
    1. "Google C ++ Stil Kılavuzu" "Sabit kullanımı" bölümü
    2. "109. Haftanın İpucu: constİşlev Bildirimlerinde Anlamlı "
    3. Adisak'ın "İşlev parametreleri için 'sabit' kullanımı" konusundaki Yığın Taşması yanıtı
  3. " Asla üst düzey const[yani: değere göre geçirilenconst parametrelerde ], tanım olmayan tanımlarda işlev parametrelerinde (ve anlamsız bir şekilde kopyalayıp yapıştırmamaya dikkat edin ). Derleyici tarafından anlamsız ve yok sayılır, görsel gürültüdür ve okuyucuları yanlış yönlendirebilir "( https://abseil.io/tips/109 , vurgu eklendi). const
    1. constDerleme üzerinde etkisi olan tek niteleyiciler, bir üstbilgi dosyasındaki bir işlev (yöntem) bildirimi gibi, işlevin ileri bir bildiriminde değil, işlev tanımına yerleştirilen niteleyicilerdir.
  4. Bir işlev tarafından döndürülen değerlerde asla üst düzey const[yani: değere göre iletilenconst değişkenlerde ] kullanmayın .
  5. constBir işlev tarafından döndürülen işaretçiler veya başvurularda kullanmak bazen yararlı olabileceğinden uygulayıcıya bağlıdır .
  6. YAPILACAKLAR: Yukarıdakilerden bazılarını aşağıdaki clang-tidyseçeneklerle uygulayın:
    1. https://clang.llvm.org/extra/clang-tidy/checks/readability-avoid-const-params-in-decls.html
    2. https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

constYukarıda açıklanan kuralları göstermek için bazı kod örnekleri :

constParametre Örnekleri:
(bazıları buradan ödünç alınır )

void f(const std::string);   // Bad: const is top level.
void f(const std::string&);  // Good: const is not top level.

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]

constDönüş Türü Örnekleri:
(bazıları buradan ödünç alınır )

// BAD--do not do this:
const int foo();
const Clazz foo();
Clazz *const foo();

// OK--up to the implementer:
const int* foo();
const int& foo();
const Clazz* foo();

[KOPYA / MACUN SONU]


2

Bahsetmeniz durumunda, API'nızın arayanlarını etkilemez, bu yüzden yaygın olarak yapılmaz (ve başlıkta gerekli değildir). Yalnızca işlevinizin uygulanmasını etkiler.

Yapılması özellikle kötü bir şey değildir, ancak API'nızı etkilemediği ve yazmayı eklediği için faydaları o kadar da büyük değildir, bu yüzden genellikle yapılmaz.


2

Ben const değer geçirilen parametre için kullanmıyorum. Arayan kişi parametreyi değiştirip değiştirmemenizle ilgilenmez, bu bir uygulama detayıdır.

Gerçekten önemli olan, örneklerini değiştirmedikleri takdirde yöntemleri const olarak işaretlemektir. Aksi halde const_cast <> ile sonlanabilir veya bir yöntem const işaretlemenin const olarak işaretlenmiş olması gereken diğer yöntemleri çağırdığı için çok sayıda kodun değiştirilmesini gerektirdiğini görebilirsiniz.

Ayrıca bunları değiştirmek gerekmiyorsa yerel vars const işaretlemek eğilimindedir. Ben "hareketli parçaları" tanımlamayı kolaylaştırarak kodun anlaşılmasını kolaylaştırdığına inanıyorum.



2

Yapabileceğim const kullanıyorum. Parametreler için sabit, değerlerini değiştirmemesi gerektiği anlamına gelir. Bu özellikle referansla geçerken değerlidir. const for function, işlevin sınıf üyelerini değiştirmemesi gerektiğini bildirir.


2

Özetlemek:

  • "Normalde cons-by-pass değeri işe yaramaz ve en iyi ihtimalle yanıltıcıdır." Gönderen GOTW006
  • Ancak bunları .cpp dosyasına değişkenlerle yaptığınız gibi ekleyebilirsiniz.
  • Standart kitaplığın const kullanmadığını unutmayın. Örn std::vector::at(size_type pos). Standart kütüphane için yeterince iyi olan benim için iyidir.

2
"Standart kütüphane için yeterince iyi olan benim için iyidir" her zaman doğru değildir. Örneğin, standart kütüphane _Tmpher zamanki gibi çirkin değişken adları kullanır - bunu istemezsiniz (aslında bunları kullanmanıza izin verilmez).
anatolyg

1
@anatolyg bu bir uygulama detayıdır
Fernando Pelliccioni

2
Tamam, hem değişken adları hem de bağımsız değişken listelerindeki sabit nitelikli türler uygulama ayrıntılarıdır. Söylemek istediğim, standart kütüphanenin uygulanması bazen iyi değil. Bazen daha iyisini yapabilirsin ve yapmalısın. Standart kütüphane kodu ne zaman yazıldı - 10 yıl önce? 5 yıl önce (bazı yeni bölümleri)? Bugün daha iyi kod yazabiliriz.
anatolyg

1

Parametre değere göre iletilirse (ve bir referans değilse), genellikle parametrenin const olarak bildirilip bildirilmediği konusunda çok fazla fark yoktur (bir referans üyesi içermediği sürece - yerleşik türler için bir sorun değildir). Parametre bir referans veya işaretçi ise, genellikle işaretçinin kendisini değil, referans / işaretli belleği korumak daha iyidir (bence referansın kendisini sabitleyemezsiniz, hakemi değiştiremeyeceğiniz kadar önemli değil) . Const olarak yapabileceğiniz her şeyi korumak iyi bir fikir gibi görünüyor. Parametreler yalnızca POD'lar (yerleşik tipler dahil) ise ve yol boyunca daha fazla değişme şansı yoksa (örneğin, örneğinizde bool parametresinde) hata yapma korkusu olmadan atlayabilirsiniz.

.H / .cpp dosya bildirimi farkını bilmiyordum, ancak bir anlam ifade ediyor. Makine kodu düzeyinde hiçbir şey "const" değildir, bu nedenle bir işlevi (.h'de) const olmayan olarak bildirirseniz, kod const (dekarasyonlar) olarak bildirdiğinizle aynıdır. Ancak, derleyiciyi işlevin (.ccp) uygulanması içindeki değişkenin değerini değiştirmeyeceğinizi kaydetmenize yardımcı olur. Değişime izin veren bir arabirimden miras aldığınızda kullanışlı olabilir, ancak gerekli işlevselliği elde etmek için parametreye değiştirmeniz gerekmez.


0

Ben böyle parametreler üzerinde const koymak olmaz - herkes zaten bir boolean (bir boolean yerine) sabit olduğunu biliyor, bu yüzden eklemek eklemek "bekle, ne?" veya parametreyi referans olarak geçiyor olsanız bile.


4
bazen bir nesneyi referans olarak iletmek (performans nedenleriyle) istersiniz, ancak onu değiştirmek istemezsiniz, bu nedenle const zorunludur. Böyle tüm parametreleri - hatta bools - const tutmak, iyi bir uygulama olacaktır ve kodunuzun okunmasını kolaylaştıracaktır.
gbjbaanb

0

const ile hatırlanması gereken şey, şeyleri başlangıçtan const yapmanın, daha sonra bunları denemekten çok daha kolay olmasıdır.

Bir şeyin değişmesini istemediğinizde const kullanın - bu, işlevinizin ne yaptığını ve ne olacağını açıklayan ek bir ipucudur. Bazıları, özellikle c-dizeleri kabul olanlar ile yapabileceği bir C API gördüm!

Cpp dosyasındaki const anahtar sözcüğünü başlıktan daha fazla atlamaya meyilli olurum, ancak onları kesip yapıştırmaya eğilimli olduğum için, her iki yerde de tutulurlardı. Derleyicinin buna izin verdiğine dair hiçbir fikrim yok, sanırım bu derleyici bir şey. En iyi yöntem, const anahtar kelimenizi her iki dosyaya da koymaktır.


Bunu hiç anlamadım. Neden cpp dosyasında (işlev tanımı) atlamayı tercih edersiniz? Burası aslında bir şey ifade ediyor ve hataları yakalayabiliyor. Sizce neden her iki yere de const koymak en iyi uygulamadır? Başlık dosyasında (işlev bildirimi), hiçbir şey ifade etmez ve API'yi kümelendirir. Belki de decl ve defn'in tamamen aynı görünmesi için küçük bir değer var, ancak bana öyle geliyor ki, API'nin dağınıklığı konusuna kıyasla gerçekten küçük bir fayda.
Don Hatch

@DonHatch 8 yıl sonra, vay canına. Her neyse, OP'nin dediği gibi "Bir işlev bildirimindeki parametrelerden sabitlemeyi atlayabileceğinizi, ancak işlev tanımına dahil edebileceğinizi öğrenmek için şaşırdım".
gbjbaanb

0

İşlev bir değişkenin yine de bir kopyasını değiştirebileceğinden, bir value-parametresi "const" yapmak için hiçbir neden yoktur.

"Const" kullanmanın nedeni, referans olarak daha büyük bir şey (örn. Çok sayıda üyeli bir yapı) geçiriyorsanız, bu durumda işlevin değiştirememesini sağlar; daha doğrusu, geleneksel yolla değiştirmeye çalıştığınızda derleyici şikayet edecektir. Yanlışlıkla değiştirilmesini önler.


0

Const parametresi yalnızca parametre referans olarak iletildiğinde, yani başvuru veya işaretçi olduğunda kullanışlıdır. Derleyici bir const parametresi gördüğünde, parametrede kullanılan değişkenin işlev gövdesi içinde değiştirilmediğinden emin olun. Neden bir sabit değer parametresi yapmak istesin ki? :-)


Bir çok sebepten ötürü. Bir by-value parametresi constyapmak açıkça belirtmektedir: 'Bunu değiştirmeme gerek yok, bu yüzden bunu beyan ediyorum. Daha sonra değiştirmeye çalışırsam, hatamı düzeltebilmem veya işaretini kaldırmam için derleme zamanı hatası ver const. ' Bu yüzden hem kod hijyeni hem de güvenlik meselesi. Uygulama dosyalarına eklemek için gereken her şey, insanların saf bir refleks, IMO olarak yaptıkları bir şey olmalıdır.
underscore_d

0

Parametreler değere göre iletildikçe, çağıran işlevin perspektifinden const belirtip belirtmediğinizde herhangi bir fark yaratmaz.


0

Örneklerinizdeki tüm ifadelerin bir amacı yoktur. C ++ varsayılan olarak by-pass değeridir, bu nedenle işlev bu içeri ve boolean'ların kopyalarını alır. İşlev bunları değiştirse bile, arayanın kopyası etkilenmez.

Bu yüzden ekstra consts önlemek istiyorum çünkü

  • Onlar indirgendir
  • Metni karıştırıyorlar
  • Yararlı ya da verimli olabileceği durumlarda aktarılan değeri değiştirmemi engellerler.

-1

Sorunun "biraz" modası geçmiş olduğunu biliyorum ama karşı karşıya geldiğimde başka biri de gelecekte yapabilir ... ... yine de zavallı adamın yorumumu okumak için burada listelediğinden şüpheliyim :)

Bana öyle geliyor ki hala C tarzı düşünce tarzıyla sınırlı kalıyoruz. OOP paradigmasında, tiplerle değil nesnelerle oynarız. Sabit nesne, özellikle mantıksal sabit anlamında (bitsel-sabitin aksine), sabit olmayan bir nesneden kavramsal olarak farklı olabilir. Bu nedenle, işlev parametrelerinin sabit doğruluğu (belki de) POD'larda aşırı dikkatlilik olsa bile, nesneler için böyle değildir. Bir işlev bir const nesnesiyle çalışıyorsa, bunu söylemelidir. Aşağıdaki kod snippet'ini düşünün

#include <iostream>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SharedBuffer {
private:

  int fakeData;

  int const & Get_(int i) const
  {

    std::cout << "Accessing buffer element" << std::endl;
    return fakeData;

  }

public:

  int & operator[](int i)
  {

    Unique();
    return const_cast<int &>(Get_(i));

  }

  int const & operator[](int i) const
  {

    return Get_(i);

  }

  void Unique()
  {

    std::cout << "Making buffer unique (expensive operation)" << std::endl;

  }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void NonConstF(SharedBuffer x)
{

  x[0] = 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConstF(const SharedBuffer x)
{

  int q = x[0];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main()
{

  SharedBuffer x;

  NonConstF(x);

  std::cout << std::endl;

  ConstF(x);

  return 0;

}

ps .: (const) referansının burada daha uygun olacağını ve size aynı davranışı vereceğini iddia edebilirsiniz. Tamam, doğru. Başka bir yerde görebildiğimden farklı bir tablo çiziyorum ...

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.