.Cpp dosyasında C ++ ad alanı yöntemlerini tanımlamanın doğru yolu


108

Muhtemelen bir kopyası, ama aranması kolay değil ...

Aşağıdaki gibi bir başlık verildiğinde:

namespace ns1
{
 class MyClass
 {
  void method();
 };
}

method().Cpp dosyasında birkaç şekilde tanımlandığını görüyorum :

Versiyon 1:

namespace ns1
{
 void MyClass::method()
 {
  ...
 }
}

Versiyon 2:

using namespace ns1;

void MyClass::method()
{
 ...
}

Sürüm 3:

void ns1::MyClass::method()
{
 ...
}

Bunu yapmanın 'doğru' bir yolu var mı? Bunların hepsi aynı anlama gelmediği için 'yanlış' mı?


Kodların çoğunda genellikle üçüncü sürümü görüyorum (ama nedenini bilmiyorum: D), ikinci sürüm ad alanlarının neden tanıtıldığının tam tersi sanırım.
Bay Anubis

1
İlginç bir gerçekte olduğu gibi, Visual Assist yeniden düzenleme araçları, hedef dosyada halihazırda hangi stilin kullanıldığına bağlı olarak # 1 veya # 3 kullanarak kod oluşturmaktan mutluluk duyar.
Bay Boy

Yanıtlar:


51

Sürüm 2 açık değil ve anlaşılması kolay değil çünkü hangi ad MyClassalanının ait olduğunu bilmiyorsunuz ve sadece mantıksız (sınıf işlevi aynı ad alanında değil mi?)

Sürüm 1 doğrudur çünkü ad alanında, işlevi tanımladığınızı gösterir.

Sürüm 3, ad ::alanına başvurmak için kapsam çözümleme operatörünü kullandığınız için de haklıdır . 3. sürümü tercih ederim.MyClass::method ()ns1

Bkz. Ad Alanları (C ++) . Bunu yapmanın en iyi yolu budur.


22
# 2'ye "yanlış" demek büyük bir abartıdır. Bu mantıkla, tüm sembol adları "yanlıştır" çünkü diğer kapsamlardaki diğer sembol adlarını potansiyel olarak gizleyebilirler.
Tenfour

Mantıksız. İyi derler (üzgünüm, cevapta yanlış açıkladı), ama neden ad alanının dışında bir işlevi tanımlayasınız? Okuyucunun kafasını karıştırıyor. Ayrıca, birçok ad alanı kullanıldığında, MyClass'ın hangi ad alanına ait olduğunu göstermez. Sürüm 1 ve 3 bu sorunu çözer. Sonuç olarak, bu yanlış değil, sadece belirsiz ve kafa karıştırıcı.
GILGAMESH

3
Ben, @PhoenicaMacia katılıyorum kullanarak hile korkunç ve karışıklığa yol açabilir. Bir işleci özgür bir işlev olarak uygulayan bir sınıfı düşünün, başlıkta sahip olacağınız namespace N {struct X { void f(); }; X operator==( X const &, X const & ); }, şimdi using deyimiyle cpp dosyasında üye işlevini olarak void X::f() {}tanımlayabilirsiniz, ancak tanımlarsanız X operator==(X const&, X const&)birinden farklı bir işleci tanımlamış olursunuz. başlıkta tanımlıdır (oradaki ücretsiz işlev için 1 veya 3 kullanmanız gerekir).
David Rodríguez - dribeas

1
Özellikle 1'i tercih ederim ve bağlantılı makaledeki örnek, ilk örnekte 1'in kullandığı hiçbir şeyi gerçekten çözmez, ikincisi 1 ve 3'ün bir karışımını kullanır (işlevler niteliklerle tanımlanır, ancak bunlar dış ad alanı içinde tanımlanır)
David Rodríguez - dribeas

1
3'ün 1) en iyisi olduğunu söyleyebilirim, ancak çoğu IDE'nin bu ad alanı bildiriminin içindeki her şeyi girintilemek gibi oldukça sinir bozucu bir alışkanlığı var ve bu biraz kafa karışıklığı yaratıyor.
locka

28

5 yıl sonra hem güzel görünen hem de kötü olmayan bundan bahsetmeyi düşündüm

using ns1::MyClass;

void MyClass::method()
{
  // ...
}

3
Bu en iyi cevap. En temiz görünüyor ve OP'nin Versions 1 ile ilgili sorunları istemeden ad alanına getirebilecek ve 2, istemeden de küresel alana getirebilecek sorunları ortadan kaldırıyor.
ayane_m

Evet, bu 3'ten daha az yazmanın harika bir kombinasyonudur ve yine de açıkça niyet beyan eder.
jb

14

Sürüm 4 (aşağıda) kullanıyorum çünkü sürüm 1'in (resoektif tanımın özü) ve sürüm 3'ün (azami ölçüde açık olması) avantajlarının çoğunu birleştiriyor. Ana dezavantaj, insanların buna alışık olmamasıdır, ancak teknik olarak alternatiflerden daha üstün olduğunu düşündüğüm için umursamıyorum.

Sürüm 4: ad alanı takma adlarını kullanarak tam yeterlilik kullanın:

#include "my-header.hpp"
namespace OI = outer::inner;
void OI::Obj::method() {
    ...
}

Benim dünyamda, her şey açıkça nitelendirildiği için (örneğin değişken isimleri) veya bilinen bir özelleştirme noktası olmadığı sürece (örneğin bir fonksiyon şablonunda swap ()) sık sık ad alanı takma adları kullanıyorum.


1
Ben mantık kusurludur "insanları karıştırır umurumda kalmamak daha iyi olur" düşünüyorum da, ben bu kabul etmek zorunda olduğu iç içe ad alanları için iyi bir yaklaşım.
Bay Boy

1
Mükemmel "neden-bunu-düşünmedim" fikri için +1! ("İnsanlar [yeni teknik açıdan üstün şeylere] alışkın değiller"
konusuna

Sadece emin ben, hem anlamak yapmak outerve innerzaten diğer başlık dosyalarında ad olarak tanımlanır?
dekuShrub

4

Sürüm 3, sınıf ve ad alanı arasındaki ilişkiyi, daha fazla yazım pahasına çok açık hale getirir. Sürüm 1 bundan kaçınıyor, ancak bir blokla ilişkiyi yakalıyor. Sürüm 2 bunu gizleme eğilimindedir, bu yüzden bundan kaçınırım.



3

Num.3'ü (ayrıntılı sürüm olarak da bilinir) seçiyorum. Daha çok yazıyor, ancak amaç size ve derleyiciye göre kesin. Olduğu gibi gönderdiğiniz sorun aslında gerçek dünyadan daha basit. Gerçek dünyada, sadece sınıf üyeleri değil, tanımlamalar için başka kapsamlar da vardır. Tanımlarınız yalnızca sınıflarla çok karmaşık değildir - çünkü kapsamları asla yeniden açılmamaktadır (ad alanları, genel kapsam vb. Aksine).

Num.1 bu, sınıflar dışındaki kapsamlarda başarısız olabilir - yeniden açılabilen herhangi bir şey. Bu nedenle, bu yaklaşımı kullanarak bir ad alanında yeni bir işlev ilan edebilirsiniz veya satır içi satırlarınız ODR aracılığıyla ikame edilebilir. Bazı tanımlar için buna ihtiyacınız olacak (özellikle şablon uzmanlıkları).

Num.2 Bu, özellikle büyük kod tabanlarında çok kırılgandır - başlıklar ve bağımlılıklar değiştikçe, programınız derlenemez.

Num.3 Bu ideal, ama çok yazın - sizin niyet tanımlamak için ne bir şey . Bu tam olarak bunu yapar ve derleyici hata yapmadığınızdan emin olmak için devreye girer, bir tanımın beyanıyla uyumlu olmadığından vb.



2

Tüm yollar doğrudur ve her birinin avantajları ve dezavantajları vardır.

1. sürümde, ad alanını her işlevin önüne yazmak zorunda kalmama avantajına sahipsiniz. Dezavantajı, özellikle birden fazla ad alanınız varsa, sıkıcı bir kimlik elde etmenizdir.

2. sürümde, kodunuzu daha temiz yaparsınız, ancak CPP'de uygulanan birden fazla ad alanınız varsa, biri diğerinin işlevlerine ve değişkenlerine doğrudan erişebilir ve ad alanınızı işe yaramaz hale getirir (o cpp dosyası için).

3. sürümde, daha fazla yazmanız gerekecek ve işlev çizgileriniz ekrandan daha büyük olabilir, bu da tasarım efektleri için kötüdür.

Bazı insanların onu kullanmasının başka bir yolu da var. İlk versiyona benzer, ancak tanımlama sorunları yoktur.

Bunun gibi:

#define OPEN_NS1 namespace ns1 { 
#define CLOSE_NS1 }

OPEN_NS1

void MyClass::method()
{
...
}

CLOSE_NS1

Her durum için hangisinin daha iyi olduğunu seçmek size kalmış =]


14
Burada makro kullanmak için herhangi bir sebep göremiyorum. Girinti yapmak istemiyorsanız, girinti yapmayın. Bir makro kullanmak, kodu daha az belirgin hale getirir.
57'de tenfour

1
Sanırım bahsettiğiniz son sürüm, kodunuzu ad alanlarını desteklemeyen eski derleyicilerle derlemek istediğinizde yararlıdır (Evet, bazı dinozorlar hala ortalıktadır). Bu durumda makroyu bir #ifdefcümle içine koyabilirsiniz .
Luca Martini

İstemiyorsanız tanımlamak zorunda değilsiniz, ancak makro kullanmazsanız, bazı IDE'ler bunu sizin için yapmaya çalışacaktır. Örneğin, Visual Studio'da kodun tamamını seçebilir ve otomatik tanımlama için ALT + F8 tuşlarına basabilirsiniz. Tanımları kullanmazsanız, bu işlevselliği kaybedersiniz. Ayrıca, kodlama standardınızda varsa, OPEN_ (ad alanı) ve CLOSE_ (ad alanı) 'nın daha az belirgin olduğunu düşünmüyorum. @LucaMartini'nin vermesinin nedeni de ilginç.
Renan Greinert

Bu jenerik yapıldıysa, yani biraz kullanışlı #define OPEN_NS(X)olduğunu düşünüyorum , ama gerçekten değil ... Makrolara itiraz etmiyorum ama bu biraz OTT gibi görünüyor. Dietmar Kühl'ün yaklaşımının iç içe geçmiş ad alanları için daha iyi olduğunu düşünüyorum.
Bay Boy
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.