C ++ üstbilgilerinde "ad alanını kullanma"


119

Tüm c ++ kurslarımızda, tüm öğretmenler dosyalarında her zaman ' lerin using namespace std;hemen arkasına koyarlar . O zamandan beri bu bana tehlikeli geliyor, o zamandan beri o başlığı başka bir programa dahil ederek ad alanını programıma aktarılan ad alanını, belki farkında olmadan, niyet etmeden veya istemeden elde edeceğim (başlık dahil etme çok derin bir şekilde iç içe olabilir).#include.h

Öyleyse sorum çifte: using namespaceBaşlık dosyalarında kullanılmaması gereken doğru muyum ve / veya bunu geri almanın bir yolu var mı, şöyle bir şey:

//header.h
using namespace std {
.
.
.
}

Aynı satırlar boyunca bir soru daha: Bir başlık #include, karşılık gelen .cppdosyanın ihtiyaç duyduğu tüm başlıkları dosyalamalı mı , yalnızca başlık tanımları için gerekli olanları .cppdosyalamalı #includeve dosyanın geri kalanını bırakmalı mı yoksa hiçbirini yapmalı ve ihtiyaç duyduğu her şeyi açıklamalı externmı?
Sorunun arkasındaki mantık yukarıdakiyle aynı: .hDosyaları dahil ederken sürprizler istemiyorum .

Ayrıca, eğer haklıysam, bu yaygın bir hata mıdır? Yani gerçek dünya programlamasında ve orada "gerçek" projelerde.

Teşekkür ederim.



3
bir yan not olarak, using namespaceifadeler nedeniyle ad çakışmaları yaşarsanız, sorunu çözmek için tam nitelikli adı kullanabilirsiniz.
Marius Bancila

Yanıtlar:


115

Bu using namespacebaşlığı içeren diğer dosyalarda kodun anlamını beklenmedik şekilde değiştirebileceğini tam olarak söylemenizin nedeni için kesinlikle başlıklarda KULLANMAMALISINIZ . using namespaceBu kadar tehlikeli olmasının başka bir nedeni olan bir şeyi geri almanın bir yolu yok . Daha karmaşık bir şey denemek yerine başlıklarda çağrılmadığından grepemin olmak için genellikle veya benzerini kullanırım using namespace. Muhtemelen statik kod denetleyicileri de bunu işaretler.

Başlık yalnızca derlemesi gereken başlıkları içermelidir. Bunu zorlamanın kolay bir yolu, her kaynak dosyanın kendi başlığını her zaman diğer başlıklardan önce ilk şey olarak eklemektir. Daha sonra başlık bağımsız değilse kaynak dosya derlenemez. Bazı durumlarda, örneğin bir kitaplık içindeki uygulama-ayrıntı sınıflarına atıfta bulunarak #include, bu tür ileri bildirimli sınıfın tanımı üzerinde tam denetime sahip olduğunuz için ileriye dönük bildirimleri kullanabilirsiniz .

Buna ortak diyeceğimden emin değilim, ama kesinlikle ara sıra ortaya çıkıyor, genellikle olumsuz sonuçların farkında olmayan yeni programcılar tarafından yazılıyor. Genellikle, düzeltilmesi nispeten basit olduğundan, riskler hakkında küçük bir eğitim her türlü sorunu çözer.


2
dosyalarımızda usingifade kullanmakta özgür .cppmüyüz? 3rdPartyLib::BigClassName<3rdPartyLib::AnotherBigName,3rdPartyLib::AnotherBigName>::Iterators parmak uçlarına ölümdür.
Christopher

1
ve templatebaşlıklarda olması gereken işlevleri nasıl düzene koymalıyız ? typedefs?
Christopher

1
@donlan, uzunca bir süredir yanıt alamıyorsun gibi görünüyor ... Evet, dosya usingiçindeki ifadeleri .cppfazla endişe duymadan kullanabilirsin çünkü kapsam sadece o dosyayla sınırlı olacak, ama bunu asla bir #includeifadeden önce yapmam . Başlıklarda tanımlanan şablon işlevlerine gelince, ne yazık ki sadece ad alanını yazmaktan başka iyi bir çözüm bilmiyorum ... Belki de usingayrı bir kapsamda bir bildirim koyabilirsiniz { /* using statement in between brackets */ }, bu en azından mevcut dosyadan kaçmasını engelleyecektir. .
tjwrona1992

26

Sutter ve Alexandrescu'nun "C ++ Kodlama Standartları: 101 Kurallar, Yönergeler ve En İyi Uygulamalar" daki Madde 59 :

59. Ad alanı kullanımlarını bir başlık dosyasına veya #include'dan önce yazmayın.

Ad alanları usingsizin rahatınız içindir, başkalarına zorlamanız için değildir: usingBir usingdirektiften önce asla bir beyan veya direktif yazmayın #include.

Sonuç: Başlık dosyalarında, ad alanı düzeyinde usingyönergeler veya usingbildirimler yazmayın ; bunun yerine, tüm adları açıkça ad alanını nitelendirin.

Başlık dosyası, bir veya daha fazla kaynak dosyadaki bir misafirdir. usingYönergeleri ve bildirimleri içeren bir başlık dosyası , kabadayı arkadaşlarını da getirir.

Bir using beyan , bir arkadaşı getirir. Bir using yönerge , ad alanındaki tüm arkadaşları getirir. Öğretmenlerinizin kullanımı using namespace std;bir kullanım yönergesidir.

Daha ciddisi, ad çatışmasını önlemek için ad alanlarımız var. Bir başlık dosyası, bir arabirim sağlamayı amaçlamaktadır. Başlıkların çoğu, şimdi veya gelecekte hangi kodun onları içerebileceğinden habersizdir. usingÜstbilgiye dahili kolaylık sağlamak için ifadeler eklemek , bu başlığın tüm potansiyel istemcilerinde bu uygun adları kapatır. Bu isim çatışmasına neden olabilir. Ve bu çok kaba.


12

Üstbilgilerin içine başlıklar eklerken dikkatli olmalısınız. Büyük projelerde, gerçekte gerekenden daha büyük / daha uzun yeniden yapılandırmaları tetikleyen çok karmaşık bir bağımlılık zinciri oluşturabilir. C ++ projelerinde iyi fiziksel yapının önemi hakkında daha fazla bilgi edinmek için bu makaleye ve devamına göz atın .

Başlıkları yalnızca kesinlikle gerekli olduğunda (bir sınıfın tam tanımına ihtiyaç duyulduğunda) ve mümkün olan her yerde ileriye dönük bildirimi kullanmalısınız (sınıf gerekli olduğunda bir işaretçi veya başvuru olduğunda).

İsim alanlarına gelince, başlık dosyalarımda açık ad alanı kapsamını kullanma eğilimindeyim ve using namespacecpp dosyalarıma yalnızca a koyuyorum .


1
templateişlev bildirimini nasıl kolaylaştırırsınız ? bu başlıkta gerçekleşmeli, değil mi?
Christopher

6

Goddard Uzay Uçuş Merkezi kodlama standartlarına bakın (C ve C ++ için). Bu, eskisinden biraz daha zor görünüyor - SO sorularının güncellenmiş yanıtlarına bakın:

GSFC C ++ kodlama standardı şunları söylüyor:

§3.3.7 Her başlık dosyası, #includekullanıcıları #includegerekli dosyalara zorlamak yerine, derlemesi gereken dosyaları içermelidir . #includesbaşlığın ihtiyacı olanla sınırlı olacaktır; diğeri #includeskaynak dosyaya yerleştirilmelidir.

Çapraz referanslı soruların ilki şimdi GSFC C kodlama standardından bir alıntı ve mantığı içerir, ancak madde aynıdır.


5

using namespaceBaşlıkta tehlikeli olduğu konusunda haklısın . Bunu nasıl geri alacağımı bilmiyorum. Bunu tespit etmek kolaydır, ancak sadece using namespacebaşlık dosyalarında arayın . Bu son nedenden dolayı gerçek projelerde nadirdir. Daha deneyimli iş arkadaşları, birisi böyle bir şey yaparsa yakında şikayet edecek.

Gerçek projelerde insanlar dahil edilen dosyaların miktarını en aza indirmeye çalışır, çünkü ne kadar az dahil ederseniz, o kadar hızlı derler. Bu herkesin zamanından tasarruf sağlar. Bununla birlikte, başlık dosyası bir şeyin kendisinden önce dahil edilmesi gerektiğini varsayıyorsa, o zaman kendisini içermelidir. Aksi takdirde, başlıkların kendi kendine yetmemesini sağlar.


4

Haklısın. Ve herhangi bir dosya yalnızca o dosyanın ihtiyaç duyduğu başlıkları içermelidir. "Gerçek dünya projelerinde bir şeyler yanlış yapmak yaygın mıdır?" - Oh evet!


4

Programlamadaki her şey gibi, pragmatizm de dogmatizmi, IMO'yu kazanmalıdır.

Proje genelinde karar verdiğiniz sürece ("Projemiz STL'yi kapsamlı bir şekilde kullanıyor ve her şeyin başına std ::. Eklemek istemiyoruz"), bununla ilgili sorunu görmüyorum. Riske attığın tek şey, sonuçta isim çarpışmaları ve STL'nin her yerde bulunmasıyla bir sorun olma ihtimali düşük.

Öte yandan, bir geliştiricinin tek bir (özel olmayan) başlık dosyasında verdiği bir karar olsaydı, bunun ekip arasında nasıl kafa karışıklığı yaratacağını ve kaçınılması gerektiğini görebilirim.


4

"Bir usingbildirimi geri almanın bir yolu var mı ?"

usingBeyanların kapsamdan etkilendiğini belirtmekte fayda var diye düşünüyorum .

#include <vector>

{   // begin a new scope with {
    using namespace std;
    vector myVector;  // std::vector is used
}   // end the scope with }

vector myOtherVector;   // error vector undefined
std::vector mySTDVector // no error std::vector is fully qualified

Çok etkili bir şekilde evet. usingBeyannamenin kapsamını sınırlayarak, etkisi yalnızca bu kapsam dahilinde devam eder; bu kapsam sona erdiğinde 'geri alınır'.

Ne zaman usingbeyanı başka alanda bir dosya dışarıdan bildirilmiştir o dosya kapsamı vardır ve bu dosyada her şeyi etkiler.

Bir başlık dosyası olması durumunda, usingbildirim dosya kapsamındaysa, bu, başlığın dahil olduğu herhangi bir dosyanın kapsamını genişletecektir.


2
Görünüşe göre asıl soruyu anlayan tek kişi sizsiniz ... Ancak, derlemem sınıf yavaşlamasını kullanmamdan pek memnun değil.
rustypaper

Bu cevap, OP'nin kapsamın nasıl çalışması gerektiğine dair fikrini ( namespaceaçıklama maddeleri gibi ) ve gerçekte nasıl çalıştığını (bir değişken gibi) açıklayarak daha da iyi hale getirilebilir . {}bununla {}ilgili hiçbir şey yapmadıktan sonra kapsamını sınırlar . Bu, using namespaceküresel olarak uygulanmasının tesadüfi bir yoludur .
TafT

2

Bildirimlerinizi aşağıdaki gibi iç içe bir ad alanında yazarsanız, C ++ başlıklarında 'kullanma' özelliğini güvenle kullanabileceğinize inanıyorum:

namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED
{
    /*using statements*/

    namespace DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED
    {
        /*declarations*/
    }
}

using namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED::DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED;

Bu, kullanılan ad alanları olmadan yalnızca 'DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED'de bildirilen şeyleri içermelidir. Bunu mingw64 derleyicisinde test ettim.


Bu daha önce görmediğim kullanışlı bir teknik; Teşekkürler. Normalde tam kapsam niteliğini kullanmakta ve usingişlev tanımlarının içine bildirimleri, işlevin dışındaki ad alanlarını kirletmemeleri için yapabildiğim yerlere koymak konusunda iyiydim . Ama şimdi bir başlık dosyasında C ++ 11 kullanıcı tanımlı değişmez değerleri kullanmak istiyorum ve olağan kurala göre, değişmez operatörler bir ad alanı tarafından korunur; ancak bunları, kirletici olmayan bir usingbildirimi kullanabileceğim bir kapsamda olmayan yapıcı başlatıcı listelerinde kullanmak istemiyorum . Bu, bu sorunu çözmek için harika.
Anthony Hall

Bu modelin talihsiz bir yan etkisi içteki isim alanı içinde bildirilen herhangi sınıflar tam adıyla derleyici hata mesajlarında görüneceği anlamına olmasına rağmen: error: ... DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED:: DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED::ClassName .... En azından benim için g ++ 'da olan şey bu.
Anthony Hall
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.