C ++ 'ta ad alanlarını nasıl düzgün kullanırsınız?


231

Ad alanlarından değil, paketlerin kullanıldığı Java arka planından geliyorum. Birlikte çalışan sınıfları paketlere tam bir nesne oluşturmak ve daha sonra bunları bu paketten yeniden kullanmak için alışkınım. Ama şimdi C ++ ile çalışıyorum.

C ++ 'ta ad alanlarını nasıl kullanırsınız? Tüm uygulama için tek bir ad alanı mı oluşturuyorsunuz, yoksa ana bileşenler için ad alanları mı oluşturuyorsunuz? Öyleyse, diğer ad alanlarındaki sınıflardan nasıl nesne yaratırsınız?

Yanıtlar:


167

Ad alanları temel olarak paketlerdir. Bu şekilde kullanılabilirler:

namespace MyNamespace
{
  class MyClass
  {
  };
}

Sonra kodda:

MyNamespace::MyClass* pClass = new MyNamespace::MyClass();

Veya, her zaman belirli bir ad alanı kullanmak istiyorsanız, bunu yapabilirsiniz:

using namespace MyNamespace;

MyClass* pClass = new MyClass();

Düzenleme: Bernhardrusch ne dedi sonra hiç "ad alanı x kullanma" sözdizimi kullanma eğilimindedir, genellikle açıkça nesnelerimı somutlaştırırken ad alanını belirtin (yani gösterdiğim ilk örnek).

Ve aşağıda sorduğunuz gibi , istediğiniz kadar ad alanı kullanabilirsiniz.


25
IMO, stdad alanının sembollere önek olarak kullanılmasına hiç alışmaktan daha iyidir using. Bu yüzden hep yazıyorum std::coutya da std::stringşimdi çünkü onlara şimdi böyle diyorum. Asla yazmam cout.
Tom Savage

5
Bu çok doğru olsa da std, daha küçük kütüphanelerle uğraşırken kişisel olarak bunu çok daha az önemli buldum. Genellikle using namespace FooBario;, özellikle bir kitaplıktan çok sayıda türde kullanıyorsanız kullanabilirsiniz .
jkerian

4
@jkerian, senin fikrini görüyorum, ama katılmıyorum çünkü isim çarpışmalarının (bence) tam olarak bu tür küçük kütüphanelerden gelme olasılığı daha yüksektir. Çoğu kişi, STL'dekiyle aynı sınıfları / işlevleri adlandırmamaya dikkat eder. Bununla birlikte, using namespace X;mümkünse başlık dosyalarından kaçınılması gerektiğini kabul ediyorum .
Alan Turing

12
@LexFridman "Çoğu insan sınıfları / işlevleri STL'dekiyle aynı şekilde adlandırmamaya dikkat eder" - DOĞRU DEĞİLDİR. Örneğin, bazı garip donanımlar için çok özel bir G / Ç kodu yazacak olsaydım, asla mylibrary::endlkendi özel yeni satır dizimi temsil etmekten başka hiçbir şey kullanmam . Yani, isimleri neden icat ettiniz?

Açıkça belirtmek istememe rağmen derleyicim yine de ad alanını tanımayacak ve bildirildiği dosyayı ekleyeceğim.
bgenchel

116

Her şeyi söylemekten kaçınmak için Mark Ingram zaten ad alanlarını kullanmak için küçük bir ipucu söyledi:

Üstbilgi dosyalarında "ad alanı kullanma" yönergesinden kaçının; bu, programın bu üstbilgi dosyasını içe aktaran tüm bölümleri için ad alanını açar. Uygulama dosyalarında (* .cpp) bu normalde büyük bir sorun değildir - işlev düzeyinde "ad alanı kullanma" yönergesini kullanmayı tercih etsem de.

Bence ad alanları çoğunlukla adlandırma çakışmalarını önlemek için kullanılır - kod yapınızı düzenlemek zorunda değilsiniz. C ++ programları çoğunlukla başlık dosyaları / dosya yapısı ile organize ediyorum.

Bazen ad alanları, uygulama ayrıntılarını gizlemek için daha büyük C ++ projelerinde kullanılır.

Kullanım yönergesine ek not: Bazı kişiler yalnızca tek öğeler için "kullanmayı" tercih eder:

using std::cout;  
using std::endl;

2
.Cpp dosya düzeyinde veya .cpp içinde ad alanı {} blok düzeyinde değil, önerdiğiniz gibi işlev düzeyinde "ad alanını kullanmanın" bir avantajı, tek derleme birimi derlemelerine çok yardımcı olmasıdır. "ad alanını kullanma" geçişlidir ve aynı ünitedeki ayrı {A {} blokları ad alanı A için geçerlidir, bu nedenle tek derleme birimi yapıları için dosya veya ad alanı blok düzeyinde yapılırsa her şeyi hızlı bir şekilde kullanırsınız.
idij

using std::cout; bir beyan olduğunu
Konstantin

3
Tek bir ifadede tek bir ad alanından birkaç ad kullanmak mümkün müdür ? Gibi bir şey , hatta . using std::cout, std::endl;using std::cout, endl;
AlQuemist

using namespace xBaşka bir ad alanındaysa, bir başlıkta a kullanmak uygun olabilir . Genel olarak tavsiye edeceğim bir şey değil ama küresel isim alanını kirletmiyor.
Praxeolitic

79

Vincent Robert yaptığı açıklamada doğru C ++ 'ta ad alanlarını nasıl kullanıyorsunuz? .

Ad boşluğu kullanma

İsim alanları, isim çarpışmasını önlemek için en azından kullanılır. Java'da bu, "org.domain" deyimiyle zorunlu kılınmıştır (çünkü kişinin kendi alan adından başka bir şey kullanmayacağı varsayılır).

C ++ ile, modülünüzdeki tüm koda bir ad alanı verebilirsiniz. Örneğin, MyModule.dll modülü için koduna MyModule ad alanı verebilirsiniz. Başka bir yerde MyCompany :: MyProject :: MyModule kullanan birini gördüm. Sanırım bu aşırıya kaçmış, ama sonuçta benim için doğru görünüyor.

"Using" kullanma

Bir ad alanından bir (veya tüm) sembolü geçerli ad alanınıza etkili bir şekilde içe aktardığı için kullanmak büyük bir dikkatle kullanılmalıdır.

Bu, bir başlık dosyasında yapmak için kötüdür, çünkü başlığınız dahil her kaynağı kirletecektir (bana makroları hatırlatır ...) ve hatta bir kaynak dosyada, işlev kapsamının dışındaki kötü stil, global kapsamda içe aktarılacağı için ad alanından semboller.

"Using" kullanmanın en güvenli yolu, seçili sembolleri içe aktarmaktır:

void doSomething()
{
   using std::string ; // string is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   std::cout << a << std::endl ;
}

void doSomethingElse()
{
   using namespace std ; // everything from std is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   cout << a << endl ;
}

Bir çok "namespace std kullanma;" öğretici veya örnek kodlarda. Nedeni, okumayı kolaylaştırmak için sembol sayısını azaltmaktır, çünkü bu iyi bir fikirdir.

"ad alanı std kullanarak;" Scott Meyers tarafından önerilmez (tam olarak hangi kitabı hatırlamıyorum ama gerekirse bulabilirim).

Ad Alanı Kompozisyonu

Ad alanları paketlerden daha fazlasıdır. Başka bir örnek Bjarne Stroustrup'un "C ++ Programlama Dili" nde bulunabilir.

"Özel Sürüm" de, 8.2.8 Ad Alanı Kompozisyonu'nda , iki AAA ve BBB ad alanını CCC adlı başka bir alanla nasıl birleştirebileceğinizi açıklar. Böylece CCC hem AAA hem de BBB için bir takma ad haline gelir:

namespace AAA
{
   void doSomething() ;
}

namespace BBB
{
   void doSomethingElse() ;
}

namespace CCC
{
   using namespace AAA ;
   using namespace BBB ;
}

void doSomethingAgain()
{
   CCC::doSomething() ;
   CCC::doSomethingElse() ;
}

Kendi özel ad alanı arayüzünüzü oluşturmak için farklı ad alanlarından seçilen sembolleri bile içe aktarabilirsiniz. Henüz bunun pratik bir kullanımını bulamadım, ancak teorik olarak, havalı.


Açıklığa kavuşturabilir misiniz, lütfen "modülünüzdeki tüm koda bir ad alanı verin"? Modüle kapsüllemek için iyi bir uygulama nedir. Örneğin karmaşık sayılar sınıfına ve karmaşık sayılarla ilgili dış fonksiyonlara sahibim. Bu sınıf ve bu iki fonksiyon bir isim alanında olmalı?
yanpas

74

Diğer cevaplarda bundan hiç bahsetmedim, işte 2 Kanadalı sentim:

"Ad alanını kullanma" konusunda, kullanışlı bir deyim normalde daha kısa bir ad vermek için ad alanını "yeniden adlandırmanıza" izin veren ad alanı diğer adıdır. Örneğin:

Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::TheClassName foo;
Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::AnotherClassName bar;

Yazabilirsin:

namespace Shorter = Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally;
Shorter::TheClassName foo;
Shorter::AnotherClassName bar;

55

Ad alanlarının sadece ad alanları olduğunu söyleyen herkesi dinlemeyin.

Arayüz ilkesini uygulamak için derleyici tarafından değerlendirildikleri için önemlidirler. Temel olarak, bir örnekle açıklanabilir:

namespace ns {

class A
{
};

void print(A a)
{
}

}

Bir A nesnesi yazdırmak istiyorsanız, kod şu olacaktır:

ns::A a;
print(a);

İşlevi çağırırken ad alanından açıkça bahsetmediğimizi unutmayın. Bu arabirim ilkesidir: C ++, bir türü bu tür için arabirimin bir parçası olarak bağımsız değişken olarak alan bir işlevi dikkate alır, bu nedenle parametre zaten ad alanını ima ettiği için ad alanını belirtmeye gerek yoktur.

Şimdi bu ilke neden önemlidir? A sınıfı yazarın bu sınıf için bir print () işlevi sağlamadığını düşünün. Birini kendin vermelisin. İyi bir programcı olduğunuz için, bu işlevi kendi ad alanınızda veya belki de genel ad alanında tanımlayacaksınız.

namespace ns {

class A
{
};

}

void print(A a)
{
}

Ve kodunuz, istediğiniz yerde print (a) işlevini çağırmaya başlayabilir. Şimdi düşünün, yıllar sonra, yazar sizinkinden daha iyi bir print () işlevi sağlamaya karar verir, çünkü kendi sınıfının içini bilir ve sizinkinden daha iyi bir sürüm yapabilir.

Daha sonra C ++ yazarları, arayüz ilkesine uymak için başka bir ad alanında sağlanan yerine print () işlevinin sürümünün kullanılması gerektiğine karar verdiler. Ve print () işlevinin bu "yükseltmesinin" olabildiğince kolay olması, yani print () işlevine yapılan her çağrıyı değiştirmek zorunda kalmayacağınız anlamına gelir. Bu nedenle "arabirim işlevleri" (sınıfla aynı ad alanındaki işlev) C ++ 'da ad alanını belirtmeden çağrılabilir.

Bu yüzden bir C ++ ad alanını bir arayüz kullandığınızda ve arayüz prensibini aklınızda bulundurduğunuzda düşünmelisiniz.

Bu davranışın daha iyi bir açıklamasını istiyorsanız , Herb Sutter'dan Exceptional C ++ kitabına başvurabilirsiniz.


23
Ns :: Print eklenmişse, print () için yapılan her çağrıyı gerçekten değiştirmeniz gerekir, ancak derleyici her çağrıyı belirsiz olarak işaretler. Sessizce yeni işleve geçmek korkunç bir fikir olacaktır.
Tutulma

Şimdi merak ediyorum, @Vincent'in tüm çağrıları değiştirmek zorunda kalacağını söyledikten sonra, autor ns :: Print () işlevi sağlarsa, ne söylemeye çalışıyordunuz? Yazar bir ns :: Print () işlevi eklediğinde, yalnızca kendi uygulamanızı kaldırabilirsiniz? Ya da sadece ns :: print () kullanarak -clare kullanarak ekleyebilirsiniz? Yoksa başka bir şey mi? Teşekkürler
Vaska el gato

36

Daha büyük C ++ projeleri Birden fazla ad alanı (örn. Boost kitaplığı) kullanmadım.

Aslında boost tonlarca ad alanı kullanır, genellikle boost'un her bölümü iç çalışmalar için kendi ad alanına sahiptir ve daha sonra üst düzey ad alanı desteğine sadece genel arayüzü koyabilir.

Şahsen ben bir kod-tabanı ne kadar büyük olursa, tek bir uygulama (ya da kütüphane) içinde bile daha önemli ad alanları olur. İş yerinde, uygulamamızın her modülünü kendi ad alanına yerleştiriyoruz.

Çok kullandığım ad alanlarının başka bir kullanımı (pun amaçlı değil), anonim ad alanıdır:

namespace {
  const int CONSTANT = 42;
}

Bu temel olarak aynıdır:

static const int CONSTANT = 42;

Ancak anonim bir ad alanı kullanmak (statik yerine), kod ve verilerin yalnızca C ++ 'daki geçerli derleme birimi içinde görünmesi için önerilen yöntemdir.


13
Her iki örneğiniz de eşdeğerdir, const int CONSTANT = 42;çünkü bir ad alanı kapsamındaki üst düzey sabit zaten iç bağlantı anlamına gelir. Yani bu durumda anonim bir isim alanına ihtiyacınız yoktur.
sellibitze

19

Ayrıca, bir ad alanına ekleyebileceğinizi unutmayın. Bu bir örnekle daha açık, demek istediğim şu olabilir:

namespace MyNamespace
{
    double square(double x) { return x * x; }
}

bir dosyada square.hve

namespace MyNamespace
{
    double cube(double x) { return x * x * x; }
}

bir dosyada cube.h. Bu, tek bir ad alanını tanımlar MyNamespace(yani, birden fazla dosyada tek bir ad alanı tanımlayabilirsiniz).


11

Java dilinde:

package somepackage;
class SomeClass {}

C ++ dilinde:

namespace somenamespace {
    class SomeClass {}
}

Ve bunları kullanarak Java:

import somepackage;

Ve C ++:

using namespace somenamespace;

Ayrıca, tam adlar Java için "somepackge.SomeClass" ve C ++ için "somenamespace :: SomeClass" dır. Bu kuralları kullanarak, ad alanları için eşleşen klasör adları oluşturmak da dahil olmak üzere Java'da alıştığınız gibi organize edebilirsiniz. Klasör-> paket ve dosya-> sınıf gereksinimleri yoktur, bu nedenle klasörlerinizi ve sınıflarınızı bağımsız olarak paketlerden ve ad alanlarından adlandırabilirsiniz.


6

@ marius

Evet, aynı anda birkaç ad alanı kullanabilirsiniz, örneğin:

using namespace boost;   
using namespace std;  

shared_ptr<int> p(new int(1));   // shared_ptr belongs to boost   
cout << "cout belongs to std::" << endl;   // cout and endl are in std

[Şubat. 2014 - (Gerçekten bu kadar uzun sürdü mü?): Joey'nin aşağıda belirttiği gibi bu özel örnek şimdi belirsiz. Boost ve std :: şimdi her birinin bir paylaşılan_ptr var.]


2
Şimdiye kadar stdsahip olduğuna da dikkat edin shared_ptr, bu nedenle a'yı kullanmaya çalıştığınızda hem boostve stdad alanlarını kullanmak çakışacaktır shared_ptr.
Joey

2
Bu, birçok yazılım evinin neden tüm ad alanlarını bu şekilde içe aktarmayı caydıracağına iyi bir örnektir. Her zaman ad alanını belirtmek zarar vermez ve çok uzunsa, ad alanından bir takma ad veya yalnızca önemli belirli sınıflar yapın.
Çeltik

5

Ayrıca, bir işlevin içinde "ad alanı kullanılıyor ..." da içerebilir:

void test(const std::string& s) {
    using namespace std;
    cout << s;
}

3

Genel olarak konuşursak, diğer kütüphanelerle işlev veya tür adı çakışmaları olabileceğine inanıyorsam kod gövdesi için bir ad alanı oluştururum. Ayrıca marka kodu, ala boost :: yardımcı olur .


3

Uygulama için üst düzey bir ad alanı ve bileşenler için alt ad alanları kullanmayı tercih ederim.

Diğer ad alanlarındaki sınıfları kullanma şekliniz şaşırtıcı bir şekilde java yoluna çok benzer. "Import PACKAGE" ifadesine benzeyen "use NAMESPACE" komutunu kullanabilirsiniz, örneğin std kullanın. Veya paketi "::" ile ayrılmış sınıfın öneki olarak belirtirsiniz, örneğin std :: string. Bu, Java'daki "java.lang.String" öğesine benzer.


3

C ++ 'ta bir ad alanının gerçekten sadece bir ad alanı olduğunu unutmayın. Paketlerin Java'da gerçekleştirdiği herhangi bir kapsülleme özelliğini sağlamazlar, bu nedenle muhtemelen bunları çok fazla kullanmayacaksınız.


2

C ++ ad alanlarını C #, Perl, vs.'de yaptığım gibi kullandım. Bu, standart kütüphane, üçüncü taraf şeyler ve kendi kodum arasındaki sembollerin anlamsal bir şekilde ayrılması. Kendi adımı bir ad alanına, daha sonra yeniden ayırma için başka bir ad alanında yeniden kullanılabilir bir kütüphane bileşenine yerleştirirdim.


2

Java ve C ++ arasındaki diğer bir fark, C ++ 'da ad alanı hiyerarşisinin dosya sistemi düzenini işlemek zorunda olmamasıdır. Bu nedenle, yeniden kullanılabilir bir kitaplığın tamamını tek bir ad alanına ve alt sistemlerdeki kitaplık içindeki alt sistemleri yerleştirme eğilimindeyim:

#include "lib/module1.h"
#include "lib/module2.h"

lib::class1 *v = new lib::class1();

Alt sistemleri yalnızca bir ad çakışması olasılığı varsa iç içe ad alanlarına koyardım.

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.