C ++ 'da dahili typedefs - iyi stil mi kötü stil mi?


179

Son zamanlarda kendimi sık sık bulduğum bir şey, o sınıfın içindeki belirli bir sınıfla ilgili typedef'leri, yani

class Lorem
{
    typedef boost::shared_ptr<Lorem> ptr;
    typedef std::vector<Lorem::ptr>  vector;

//
// ...
//
};

Bu türler daha sonra kodun başka bir yerinde kullanılır:

Lorem::vector lorems;
Lorem::ptr    lorem( new Lorem() );

lorems.push_back( lorem );

Beğendiğim nedenler:

  • Sınıf şablonlarının getirdiği gürültüyü azaltır, std::vector<Lorem>olur Lorem::vectorvb.
  • Bir niyet ifadesi görevi görür - yukarıdaki örnekte, Lorem sınıfının boost::shared_ptrbir vektör üzerinden sayıldığı ve saklandığı referans olması amaçlanmıştır .
  • Uygulamanın değişmesine izin verir - yani Lorem'in boost::intrusive_ptrdaha sonraki bir aşamada müdahaleci referans olarak (eğer yoluyla ) sayılması için değiştirilmesi gerekiyorsa, bunun kod üzerinde minimum etkisi olacaktır.
  • Bence 'daha güzel' görünüyor ve okunması daha kolay.

Beğenmeme nedenleri:

  • Bazen bağımlılıklar ile ilgili sorunlar vardır - Lorem::vectorbaşka bir sınıfa gömmek istiyorsanız, ancak Lorem'i (başlık dosyasına bir bağımlılık getirmenin aksine) iletmek için sadece (veya istemeniz) gerekiyorsa, biraz tutarsız olan açık tipler (örneğin, boost::shared_ptr<Lorem>yerine Lorem::ptr).
  • Çok yaygın olmayabilir ve bu nedenle anlaşılması daha zor olabilir mi?

Kodlama tarzımla objektif olmaya çalışıyorum, bu yüzden üzerinde başka fikirler almak iyi olur, böylece düşüncemi biraz parçalara ayırabilirim.

Yanıtlar:


153

Bence mükemmel bir stil ve kendim kullanıyorum. Adların kapsamını mümkün olduğunca sınırlamak her zaman en iyisidir ve sınıfların kullanımı bunu C ++ ile yapmanın en iyi yoludur. Örneğin, C ++ Standart kitaplığı sınıflar içinde typedef'leri yoğun şekilde kullanır.


Bu iyi bir nokta, merak ediyorum ki 'daha güzel' görünmek bilinçaltımın sınırlı kapsamın iyi bir şey olduğuna dikkatle dikkat çekiyordu . Yine de merak ediyorum, STL'nin ağırlıklı olarak sınıf şablonlarında kullanması onu farklı bir kullanım haline getiriyor mu? 'Somut' bir sınıfta haklı göstermek daha mı zor?
Will Baker

1
Standart kütüphane sınıflar yerine şablonlardan oluşuyor, ama gerekçe her ikisi için de aynı.

14

Bir niyet ifadesi olarak işlev görür - yukarıdaki örnekte, Lorem sınıfının, boost :: shared_ptr yoluyla sayılan ve bir vektörde saklanan referans olması amaçlanmıştır.

Bu mu tam olarak ne değildir yapmak.

Kodda 'Foo :: Ptr' görürsem, bir paylaşılan_ptr veya bir Foo * (STL'nin T * olan işaretçi typedefs veya hatırlayın) olup olmadığı konusunda hiçbir fikrim yok. Esp. paylaşılan bir işaretçi ise, ben hiçbir typedef sağlamaz, ancak kodda shared_ptr kullanımını açık tutun.

Aslında, hiç bir zaman Meta Metagramlama dışında typedefs kullanmak.

STL bu tür şeyleri her zaman yapar

Üye işlevleri ve iç içe tip tanımları açısından tanımlanan kavramlara sahip STL tasarımı, tarihi bir çıkmaz noktasıdır, modern şablon kütüphaneleri, serbest işlevleri ve özellik sınıflarını (cf. Boost.Graph) kullanır, çünkü bunlar yerleşik türleri kavramın modellenmesi ve verilen şablon kütüphanelerinin kavramları göz önünde bulundurularak tasarlanmayan türlerin uyarlanmasını kolaylaştırır.

STL'yi aynı hataları yapmak için bir sebep olarak kullanmayın.


İlk bölümünüze katılıyorum, ancak son düzenlemeniz biraz kısa görüşlü. Bu tür yuvalanmış türler, mantıklı bir temerrüt sağladıkları için özellik sınıflarının tanımını basitleştirir. Yeni std::allocator_traits<Alloc>sınıfı düşünün ... yazdığınız her bir ayırıcı için uzmanlaşmanıza gerek yok, çünkü sadece türleri doğrudan ödünç alıyor Alloc.
Dennis Zickefoose

@Dennis: C ++ 'da kolaylık, / yazarının / tarafında değil, bir kullanıcı / kütüphane tarafında olmalıdır: kullanıcı bir özellik için tek tip bir arabirim ister ve yalnızca bir özellik sınıfı bunu verebilir, yukarıda verilen nedenlerden dolayı). Ancak bir Allocyazar olarak bile std::allocator_traits<>, yeni türü için uzmanlaşmak , gerekli tip tanımlarını eklemekten daha zor değil . Cevabı da düzenledim, çünkü tam cevabım bir yoruma uymadı.
Marc Mutz - mmutz

Ama bir kullanıcı tarafında. Bir itibariyle kullanıcıya ait allocator_traitsözel bir ayırıcısı oluşturulmaya çalışılırken, yapmam gereken tek şey verir ki ... özellikleri sınıfının on beş üye ile rahatsız gerekmez typedef Blah value_type;ve uygun üye işlevleri sağlamak ve varsayılan allocator_traitsçözemezsin dinlenme. Ayrıca, Boost.Graph örneğinize bakın. Evet, özellik sınıfını yoğun bir şekilde kullanıyor ... ancak kendi dahili typedef'leri için graph_traits<G>basitçe sorguların varsayılan uygulaması G.
Dennis Zickefoose

1
Ve 03 standart kütüphane bile uygun olduğunda özellik sınıflarından yararlanır ... kütüphanenin felsefesi kaplarda genel olarak değil, yineleyiciler üzerinde çalışmaktır. Böylece iterator_traits, genel algoritmalarınızın uygun bilgileri kolayca sorgulayabilmesi için bir sınıf sağlar . Hangi, tekrar, kendi bilgi için yineleyici sorgulama varsayılan. Uzun ve kısa olanı, özellikler ve iç tip tanımlarının birbirlerini pek de dışlamadığıdır ... birbirlerini destekliyorlar.
Dennis Zickefoose

1
@Dennis: iterator_traitsgerekli hale geldi çünkü T*bir model olmalı RandomAccessIterator, ancak gerekli typedef'leri koyamazsınız T*. Bir kez biz iterator_traits, iç içe tip tanımları gereksiz hale geldi ve keşke orada ve sonra kaldırılmış olsaydı. Aynı nedenden ötürü (dahili typedef'leri eklemek imkansızdır), T[N]STL Sequencekonseptini modellemez ve gibi kludge'lara ihtiyacınız vardır std::array<T,N>. Boost.Range, T[N]iç içe tip tanımları veya üye işlevleri gerektirmediği için modelleme yapabilen modern bir Sıra kavramının nasıl tanımlanabileceğini gösterir .
Marc Mutz - mmutz


8

Typdefs kesinlikle iyi bir stil. Ve tüm "sevdiğim nedenler" iyi ve doğrudur.

Bununla ilgili yaşadığınız sorunlar hakkında. İleri beyan, kutsal bir kâse değildir. Çok düzeyli bağımlılıkları önlemek için kodunuzu tasarlayabilirsiniz.

Typedef'i sınıfın dışına taşıyabilirsiniz ancak Class :: ptr, ClassPtr'den çok daha güzel, bunu yapmıyorum. Benim gibi isim alanları gibi - işler kapsam içinde bağlı kalır.

Bazen yaptım

Trait<Loren>::ptr
Trait<Loren>::collection
Trait<Loren>::map

Ve tüm etki alanı sınıfları için varsayılan ve belirli olanlar için bazı uzmanlık ile olabilir.


3

STL her zaman bu tür bir şey yapar - typedefs, STL'deki birçok sınıf için arabirimin bir parçasıdır.

reference
iterator
size_type
value_type
etc...

hepsi çeşitli STL şablon sınıfları için arabirimin bir parçası olan typedef'lerdir.


Doğru ve sanırım ilk aldığım yer burası. Bunları haklı çıkarmak biraz daha kolay gibi görünüyor. 'Meta-programlama' hattı boyunca düşünürseniz, bir sınıf şablonu içinde typedefs değişkenlere daha benzer olarak görüntülemek yardımcı olamaz.
Will Baker

3

Bunun başka bir oyu iyi bir fikir. Bunu hem zaman hem de mekanda verimli olması gereken bir simülasyon yazarken yapmaya başladım. Tüm değer türlerinde, boost paylaşılan işaretçi olarak başlayan bir Ptr typedef vardı. Daha sonra bazı profilleme yaptım ve bazılarını bu nesnelerin kullanıldığı kodlardan herhangi birini değiştirmek zorunda kalmadan bir artırıcı müdahaleci işaretçiye değiştirdim.

Bunun yalnızca sınıfların nerede kullanılacağını bildiğinizde ve tüm kullanımların aynı gereksinimlere sahip olduğunu unutmayın. Örneğin, kütüphane kodunda kullanmam, çünkü kütüphaneyi hangi bağlamda kullanacağınızı bilemezsiniz.


3

Şu anda bu tür typedefs kullanan kod üzerinde çalışıyorum. Şimdiye kadar bu iyi.

Ancak oldukça sık yinelemeli typedefler olduğunu, tanımların birkaç sınıf arasında bölündüğünü fark ettim ve hangi türle uğraştığınızı asla bilemezsiniz. Benim görevim bu typedef'lerin arkasına gizlenmiş bazı karmaşık veri yapılarının boyutunu özetlemek - bu yüzden mevcut arayüzlere güvenemiyorum. Üç ila altı seviyeli iç içe geçmiş ad alanları ile birlikte kafa karıştırıcı hale gelir.

Bu yüzden onları kullanmadan önce dikkate alınması gereken bazı noktalar var

  • Bu tip tanımlara ihtiyaç duyan var mı? Sınıf diğer sınıflar tarafından çok kullanılıyor mu?
  • Kullanımı kısaltır mıyım veya sınıfı gizler miyim? (Saklanması durumunda arayüzleri de düşünebilirsiniz.)
  • Diğer kişiler kodla çalışıyor mu? Nasıl yapıyorlar? Daha kolay olduğunu düşünecekler mi, yoksa karışacaklar mı?

1

Bu typedef'leri sınıfın dışına taşımanızı öneririm. Bu şekilde, paylaşılan işaretçi ve vektör sınıflarına doğrudan bağımlılığı kaldırır ve bunları yalnızca gerektiğinde dahil edebilirsiniz. Sınıf uygulamanızda bu türleri kullanmadığınız sürece, bunların iç typedefler olmaması gerektiğini düşünüyorum.

Hoşunuza giden nedenler hala eşleştirilmiştir, çünkü bunlar sınıfınız içinde bildirmekle değil, typedef ile tür takma adıyla çözülür.


Bu anonim ad alanını typedef'lerle kirletir değil mi ?! Typedef ile ilgili sorun, bulması / düzeltmesi zor olan birden fazla modülde / tarafından dahil edildiğinde çakışmalara neden olabilecek gerçek türü gizlemesidir. Bunları ad alanlarında veya sınıfların içinde tutmak iyi bir uygulamadır.
Indy9000

3
Ad çakışmaları ve adsız ad alanı kirliliğinin, bir sınıfın içinde veya dışında bir yazım adının tutulmasıyla ilgisi yoktur. Tip tanımlarınızla değil, sınıfınızla bir ad çakışması yaşayabilirsiniz. Bu nedenle ad kirliliğini önlemek için ad alanlarını kullanın. Sınıfınızı ve ilgili tip tanımlarını bir ad alanında bildirin.
Cătălin Pitiș

Typedef'i bir sınıfın içine yerleştirmek için başka bir argüman, tempe edilmiş fonksiyonların kullanılmasıdır. Örneğin, bir işlev, bilinmeyen bir dize türü (dize veya kendi dize uyumlu varyantınız) içeren bilinmeyen bir kapsayıcı türü (vektör veya liste) aldığında. konteyner yükünün türünü anlamanın tek yolu, konteyner sınıfı tanımının bir parçası olan typedef 'value_type' dir.
Marius

1

Typedef sadece sınıfın içinde kullanıldığında (yani özel olarak ilan edilir) iyi bir fikir olduğunu düşünüyorum. Ancak, tam olarak verdiğiniz nedenlerden ötürü, typedef'in sınıf dışında bilinmesi gerekiyorsa bunu kullanmam. Bu durumda onları sınıfın dışına taşımanızı öneririm.

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.