statik sabit vs # tanım


212

static constDeğişkenleri kullanmak #defineönişlemciden daha mı iyidir ? Ya da belki bağlama bağlıdır?

Her yöntemin avantajları / dezavantajları nelerdir?


14
Scott Meyers bu konuyu çok güzel ve kapsamlı bir şekilde ele alıyor. "Effective C ++ Third Edition" da 2. maddesi. Sınıfa özgü sabitler için bir sınıf kapsamında iki özel durum (1) statik sabit tercih edilir; (2) #define göre ad alanı veya anonim kapsam sabit tercih edilir.
Eric

2
Numaraları tercih ederim. Çünkü her ikisinin de melezi. Değişken oluşturmazsanız yer işgal etmez. Sadece sabit olarak kullanmak istiyorsanız, enum en iyi seçenektir. C / C ++ 11 std'de tip güvenliği ve mükemmel bir sabittir. #define tür güvensizdir, derleyici onu optimize edemezse const yer alır.
siddhusingh

1
Kararım kullanımına olsun #defineveya static consttarafından tahrik edilmektedir (dizeleri) başlatma (aşağıda cevapların tamamını belirtilmeyen) yönü: Sabit özellikle derleme birimi içinde kullanıldığı takdirde, sadece o zaman ben gitmek static const, başka ben kullanımı #define- kaçının statik sipariş başlatma fiyasko isocpp.org/wiki/faq/ctors#static-init-order
Martin Dvorak

Eğer const, constexprya enumya senin durumunda herhangi varyasyon çalışmaları, ardından tercih#define
Phil1970

@MartinDvorak " statik sipariş başlatma fiasco önlemek " Bu sabitler için bir sorun nedir?
curiousguy

Yanıtlar:


139

Şahsen, önişlemciden nefret ediyorum, bu yüzden her zaman birlikte giderdim const.

A'nın ana avantajı #define, programınızda saklamak için bellek gerektirmemesidir, çünkü bazı metinleri gerçekte bir değerle değiştirir. Ayrıca hiçbir türü olmaması avantajına sahiptir, bu nedenle uyarı üretmeden herhangi bir tamsayı değeri için kullanılabilir.

" const" Lerin avantajları, kapsam dahiline alınabilmeleridir ve bir nesneye yönelik bir işaretçinin geçirilmesi gereken durumlarda kullanılabilirler.

staticGerçi " " kısmı ile ne elde ettiğinizi tam olarak bilmiyorum . Global olarak beyan ediyorsanız, kullanmak yerine isimsiz bir isim alanına koyarım static. Örneğin

namespace {
   unsigned const seconds_per_minute = 60;
};

int main (int argc; char *argv[]) {
...
}

8
Dize sabitleri özellikle #define, en azından daha büyük dize sabitleri için "yapı taşları" olarak kullanılabiliyorsa, d olmanın avantajlarından biri olabilir . Bir örnek için cevabıma bakın.
09:00 AnT

62
#defineHerhangi belleği kullanarak avantajı yanlış. Örnekteki "60", static constveya olmadığına bakılmaksızın bir yerde saklanmalıdır #define. Aslında, #define kullanmanın büyük (salt okunur) bellek tüketimine neden olduğu derleyiciler gördüm ve statik const gereksiz bellek kullanmıyordu.
Gilad Naor

3
Bir #define yazmış gibi, bu yüzden kesinlikle bellekten gelmiyor.
Muhterem

27
@theReverend Değişmez değerler bir şekilde makine kaynaklarını tüketmekten muaftır? Hayır, sadece farklı şekillerde kullanabilirler, belki yığın veya yığın üzerinde görünmez, ancak bir noktada program, derlenmiş tüm değerlerle birlikte belleğe yüklenir.
Sqeaky

13
@ gilad-naor, Genel olarak haklısınız, ancak 60 gibi küçük tamsayılar bazen bir tür kısmi istisna olabilir. Bazı komut kümeleri, tamsayıları veya tamsayıların bir alt kümesini doğrudan komut akışında kodlama yeteneğine sahiptir. Örneğin MIP'ler hemen eklenir ( cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/addi.html ). Bu tür bir durumda, #defined tamsayı gerçekten boşluk kullanmaz diyebilir, çünkü derlenmiş ikili dosyada zaten var olması gereken talimatlarda birkaç yedek bit kullanır.
ahcox

242

Kullanıma bağlı olarak #defines, consts ve (unuttuklarınız) enums arasındaki artılar ve eksiler :

  1. enums:

    • sadece tamsayı değerler için mümkündür
    • özellikle kapsamlı numaralandırmaların enum class Xkapsam tarafından netleştirildiği C ++ 11 numaralandırma sınıflarında düzgün işlenmiş / tanımlayıcı çatışma sorunlarıX::
    • güçlü bir şekilde yazılmıştır, ancak C ++ 03 üzerinde denetiminiz olmayan yeterince büyük işaretli veya işaretsiz bir int boyutuna (enum struct / class / union), ancak C ++ 11 varsayılan olarak intprogramlayıcı tarafından belirlenir ancak programcı tarafından açıkça ayarlanabilir
    • adres alınamıyor - numaralandırma değerleri kullanım noktalarında etkin bir şekilde yerinde değiştirildiğinden bir tane yok
    • daha güçlü kullanım sınırlamaları (örn. artan - template <typename T> void f(T t) { cout << ++t; }derlenmeyecektir, ancak bir enum'u örtülü kurucu, döküm operatörü ve kullanıcı tanımlı operatörlerle bir sınıfa sarabilirsiniz)
    • her sabitin türü çevreleyen numaradan alınır, bu nedenle template <typename T> void f(T)her biri gerçek f(int)örneklemeden farklı olan farklı numaralardan aynı sayısal değeri geçtiğinde farklı bir örnekleme elde edin. Her işlevin nesne kodu özdeş olabilir (adres ofsetleri yoksayılıyor), ancak bir derleyici / bağlayıcı gereksiz kopyaları ortadan kaldırmak için beklemem, gerçi eğer derleyici / bağlayıcı kontrol edebilirsiniz.
    • typeof / decltype ile bile, numeric_limits öğesinin anlamlı değerler ve kombinasyonlar kümesine faydalı bilgiler sağlamasını bekleyemez (aslında, "yasal" kombinasyonlar kaynak kodunda not edilmez, düşünün enum { A = 1, B = 2 }- A|Bbir program mantığından "yasal" olduğunu düşünün perspektif?)
    • enum'un tip adı RTTI, derleyici iletileri vb. gibi çeşitli yerlerde görünebilir - muhtemelen yararlı, muhtemelen gizleme
    • çeviri birimi gerçekten değeri görmeden bir numaralandırma kullanamazsınız; bu, kitaplık API'larındaki numaralandırmaların başlıkta belirtilen değerlere ihtiyacı olduğu anlamına gelir makeve diğer zaman damgası tabanlı yeniden derleme araçları, değiştirildiklerinde istemci yeniden derlemesini tetikler (kötü! )

  1. consts:

    • düzgün ele alınmış / tanımlayıcı çatışması sorunları iyi ele alındı
    • güçlü, tek, kullanıcı tanımlı tip
      • bir #defineala " yazmaya " çalışabilirsiniz #define S std::string("abc"), ancak sabit, her kullanım noktasında tekrarlanan farklı geçici sürelerin yapılmasını önler
    • One Definition Kural komplikasyonları
    • adres alabilir, onlara const referansları oluşturabilir vb.
    • constikisi arasında geçiş yaparsanız, iş ve etkiyi en aza indiren değersizliğe en çok benzeyen
    • değer uygulama dosyasının içine yerleştirilebilir, böylece yerelleştirilmiş yeniden derleme ve yalnızca istemci bağlantılarının değişikliği alması sağlanır

  1. #defines:

    • mantıklı hata mesajları yerine çözülmesi zor derleme sorunları ve beklenmedik çalışma zamanı sonuçları üretebilen, çelişkili kullanımlara daha eğilimli "küresel" kapsam /; bunu hafifletmek için şunlar gerekir:
      • uzun, belirsiz ve / veya merkezi olarak koordine edilmiş tanımlayıcılar ve bunlara erişim, kullanılan / geçerli / Koenig-aranan ad alanı, ad alanı takma adları vb.
      • En iyi uygulama, şablon parametre tanımlayıcılarının tek karakterli büyük harf olmasına izin verir (muhtemelen bir sayı gelir), küçük harf içermeyen diğer tanımlayıcıların kullanımı geleneksel olarak önişlemci tanımlamaları (OS ve C / C ++ kitaplığı dışında) için ayrılmıştır ve beklenir başlıklar). Bu, kurumsal ölçekte önişlemci kullanımının yönetilebilir kalması için önemlidir. 3. taraf kütüphanelerin uyması beklenebilir. Bunun gözlemlenmesi mevcut mutabakatların veya numaralandırmaların tanımlara / tanımlardan geçişini büyük harf kullanımında bir değişiklik gerektirir ve bu nedenle "basit" bir yeniden derleme yerine istemci kaynak kodunda düzenlemeler yapılmasını gerektirir. (Şahsen, numaralandırmaların ilk harfini büyük harfle yazıyorum, ancak consts değil, bu yüzden bu ikisi arasında da göç ediyorum - belki de yeniden düşünmek için zaman.)
    • daha fazla derleme zamanı işlemi mümkün: dizgi değişmezi bitiştirme, dizileme (boyut alma), tanımlayıcılara bitiştirme
      • Olumsuz verilen olmasıdır #define X "x"ve bazı istemci kullanım ala "pre" X "post", istersen ya yapma ihtiyacı X bir çalışma süresi değiştirilebilir değişken yerine geçiş yapan bir den daha kolaydır, oysa (yerine sadece recompilation yerine) istemci kodda düzenlemeler zorlamak sabit const char*veya const std::stringonlar verilen zaten (örneğin birleştirme işlemleri dahil etmek zorlar "pre" + X + "post"için string)
    • kullanamaz sizeoftanımlanmış bir sayısal hazır doğrudan
    • tiplenmemiş (GCC, karşılaştırıldığında karşılaştırıldığında uyarmaz unsigned)
    • bazı derleyici / bağlayıcı / hata ayıklayıcı zincirleri tanımlayıcıyı göstermeyebilir, bu nedenle "sihirli sayılara" (dizeler, her neyse ...)
    • adresi alamıyorum
    • değiştirilen değerin, #define öğesinin oluşturulduğu bağlamda, her kullanım noktasında değerlendirildiği için yasal (veya ayrık) olması gerekmez, bu nedenle henüz bildirilmeyen nesnelere başvurabilirsiniz; önceden dahil edilebilir, { 1, 2 }dizileri başlatmak için kullanılabilecek "sabitler" #define MICROSECONDS *1E-6vb. oluşturun ( kesinlikle bunu önermez!)
    • Makro ikamesi gibi __FILE__ve __LINE__bunlara dahil edilebilecek bazı özel şeyler
    • #ifşartlı olarak kodu (önişlemci tarafından seçilmediği takdirde kodun derlenmesi gerekmediği için "if" önişleminden sonra daha güçlü), #undef-ine, yeniden tanımla vb. kullanmak için ifadelerde varlığını ve değerini test edebilirsiniz .
    • değiştirilen metnin gösterilmesi gerekir:
      • tarafından kullanılan çeviri biriminde, yani istemci kullanımı için kitaplıklardaki makroların başlıkta olması gerektiği anlamına gelir makeve bu nedenle diğer zaman damgası tabanlı yeniden derleme araçları, değiştirildiklerinde istemci yeniden derlemesini tetikler (kötü!)
      • veya istemci kodunun yeniden derlendiğinden emin olmak için daha fazla dikkat gerektiren komut satırında (örneğin, tanımı sağlayan Makefile veya komut dosyası bağımlılık olarak listelenmelidir)

Kişisel fikrim:

Genel bir kural olarak, consts'yi kullanıyorum ve onları genel kullanım için en profesyonel seçenek olarak görüyorum (diğerlerinin bu eski tembel programcıya hitap eden bir sadeliği olmasına rağmen).


1
Müthiş cevap. Bir küçük nit: bazen küçük devlet makinelerinde olduğu gibi kodun netliği için başlıklarda olmayan yerel numaralandırmalar kullanıyorum. Bu yüzden her zaman başlıklarda olmak zorunda değiller.
kertme

Pro ve eksileri karışık, ben bir karşılaştırma tablosu görmek istiyorum.
Bilinmiyor123

@ Unknown123: bir tane yayınlamaktan çekinmeyin - buradan değerli hissettiğiniz herhangi bir noktayı koparmanızın bir sakıncası yok. Şerefe
Tony Delroy

48

Bu bir C ++ sorusuysa ve #definealternatif olarak bahsediyorsa , sınıf üyeleriyle değil, "global" (yani dosya kapsamı) sabitleriyle ilgilidir. C ++ ' static constda bu tür sabitler söz konusu olduğunda gereksizdir. C ++ ' constda varsayılan olarak dahili bağlantı vardır ve bunları bildirmenin bir anlamı yoktur static. Yani gerçekten constvs. ile ilgili #define.

Ve son olarak, C ++ ' constda tercih edilir. En azından bu tür sabitler yazıldığından ve kapsamlandırıldığından. Tercih basitçe hiçbir nedeni vardır #defineüzerinde constbirkaç istisna bir yana,.

Dize sabitleri, BTW, bu tür bir istisnanın bir örneğidir. İle #defined dize sabitleri de belirtildiği üzere, derleyici C / C ++ derleme zamanı birleştirme özelliği kullanabilir

#define OUT_NAME "output"
#define LOG_EXT ".log"
#define TEXT_EXT ".txt"

const char *const log_file_name = OUT_NAME LOG_EXT;
const char *const text_file_name = OUT_NAME TEXT_EXT;

PS Yine, bir durumda static constalternatif olarak bahsettiğinde #define, genellikle C ++ hakkında değil C hakkında konuştukları anlamına gelir. Bu sorunun doğru bir şekilde etiketlenip etiketlenmediğini merak ediyorum ...


1
" #define'i tercih etmek için hiçbir neden yok " Neyin üzerinde? Başlık dosyasında tanımlanan statik değişkenler?
curiousguy

9

#define beklenmedik sonuçlara yol açabilir:

#include <iostream>

#define x 500
#define y x + 5

int z = y * 2;

int main()
{
    std::cout << "y is " << y;
    std::cout << "\nz is " << z;
}

Yanlış sonuç verir:

y is 505
z is 510

Ancak, bunu sabitlerle değiştirirseniz:

#include <iostream>

const int x = 500;
const int y = x + 5;

int z = y * 2;

int main()
{
    std::cout << "y is " << y;
    std::cout << "\nz is " << z;
}

Doğru sonucu verir:

y is 505
z is 1010

Çünkü #definemetnin yerini alır. Bunu yapmak işlem sırasını ciddi şekilde bozabileceğinden, bunun yerine sabit bir değişken kullanmanızı tavsiye ederim.


1
Farklı bir beklenmedik sonuç elde ettim: ydeğeri 5500, küçük bir endian birleşimi xve 5 vardı.
Hammer ile kodlar

5

Statik bir sabit kullanmak, kodunuzdaki diğer sabit değişkenleri kullanmak gibidir. Bu, derleme öncesi işlemdeki kodda değiştirilecek bir #defin yerine, bilgilerin nereden geldiğini izleyebileceğiniz anlamına gelir.

Bu soru için C ++ SSS Lite'a bir göz atmak isteyebilirsiniz: http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.7


4
  • Statik bir sabit yazılır (bir türü vardır) ve derleyici tarafından geçerlilik, yeniden tanımlama vb. İçin kontrol edilebilir.
  • #define ne olursa olsun tanımsız olarak yeniden tanımlanabilir.

Genellikle statik sabitleri tercih etmelisiniz. Hiçbir dezavantajı yoktur. Önişlemci esas olarak koşullu derleme için kullanılmalıdır (ve bazen gerçekten kirli testler için).


3

Önişlemci direktifini kullanarak sabitleri tanımlamak #definesadece içine değil C++, içine de uygulanması tavsiye edilmez C. Bu sabitlerin türü olmayacaktır. Hatta sabitler Ciçin kullanılması önerildi const.



2

Dil özelliklerini her zaman önişlemci gibi bazı ek araçlarda kullanmayı tercih edin.

ES.31: Sabitler veya "işlevler" için makro kullanmayın

Makrolar önemli bir hata kaynağıdır. Makrolar normal kapsam ve tür kurallarına uymaz. Makrolar argüman geçişi için genel kurallara uymazlar. Makrolar, insan okuyucunun derleyicinin gördüklerinden farklı bir şey görmesini sağlar. Makrolar takım oluşturmayı karmaşıklaştırır.

Gönderen C ++ Çekirdek Yönergeleri


0

Sınıfın tüm örnekleri arasında paylaşılacak bir sabit tanımlıyorsanız, statik sabit kullanın. Sabit her örneğe özgü ise, sadece const kullanın (ancak sınıfın tüm kurucularının başlatma listesinde bu sabit üye değişkenini başlatması gerektiğini unutmayın).

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.