Başlık dosyasında ne olmalı ve ne olmamalıdır? [kapalı]


71

Hangi şeyler kesinlikle bir başlık dosyasına dahil edilmemelidir?

Örneğin, çok fazla sabiti olan belgelenmiş bir endüstri standardı formatı ile çalışıyorsam, bunları bir başlık dosyasında tanımlamak iyi bir uygulama mıdır (eğer bu format için bir çözümleyici yazıyorsam)?

Başlık dosyasına hangi fonksiyonlar girmelidir?
Hangi fonksiyonlar olmamalı?


1
Kısa ve ağrısız: Birden fazla modülde ihtiyaç duyulan tanım ve bildirimler.
ott--

21
Bu soruyu "çok geniş" olarak işaretlemek ve kapatmak, ılımlılığın mutlak utanç verici bir değeridir. Bu soru tam olarak ne aradığımı soruyor - Soru iyi oluşuyor ve çok net bir soru soruyor: En iyi uygulamalar nelerdir? Bu, yazılım mühendisliği için "çok geniş" ise ... bu forumun tamamını kapatabiliriz.
Gewure

TP; DR. C ++ için, Bjarne Stroustrup (yaratıcısı) tarafından yazılan "C ++ Programlama Dili" nin dördüncü baskısında, Bölüm 15.2.2'de bir başlığın ne içermesi ve içermemesi gerektiği açıklanmaktadır. Soruyu C olarak etiketlediğini biliyorum, ancak önerilerin bir kısmı da uygulanabilir. Bence bu iyi bir soru ...
horro

Yanıtlar:


57

Başlıklara neler yazılmalı:

  • #includeÜstbilgi bazı kaynak dosyalara dahil edildiğinde üstbilgiyi derlenebilir hale getirmek için gereken en küçük yönergeler kümesi .
  • Önişlemci, paylaşılması gereken ve yalnızca önişlemci ile yapılabilecek şeylerin tanımlarını tanımlar. C de olsa, önişlemci sembolleri en azda tutulmalıdır.
  • Yapının tanımlarını, fonksiyon prototiplerini ve başlığın gövdesindeki global değişken bildirimlerini derlenebilir hale getirmek için gereken yapıların ileri bildirimleri.
  • Birden fazla kaynak dosya arasında paylaşılan veri yapılarının ve numaralandırmaların tanımları.
  • Tanımları bağlayıcıya görünür olacak işlevler ve değişkenler için bildirimler.
  • Satır içi işlev tanımları, ancak burada dikkat edin.

Bir başlığa ait olmayan ne:

  • Bedava #includedirektifler. Bu bedava olanlar, yeniden derlenmesi gerekmeyen şeylerin yeniden derlenmesine neden oluyor ve bazen sistemin derleyememesini sağlayabilir. Yapma #includebaşlığı kendisi o diğer başlık dosyasını ihtiyacı yoksa bir başlık bir dosyayı.
  • Amacı bazı mekanizmalar, önişleyiciler dışındaki herhangi bir mekanizma ile gerçekleştirilebilecek önişlemci sembolleri.
  • Çok ve çok sayıda yapı tanımı. Bunları ayrı başlıklara ayırın.
  • Ek gerektiren #include, değişebilen veya çok büyük olan işlevlerin satır içi tanımları . Bu satır içi işlevler, herhangi bir fan çıkışı durumunda çok az olmalıdır ve eğer fanları varsa, başlıkta tanımlanan öğelere yerelleştirilmelidir.

Minimum ifade kümesini ne oluşturur #include?

Bu önemsiz bir soru olarak ortaya çıkıyor. Bir TL; DR tanımı: Bir başlık dosyası, doğrudan kullanılan ya da söz konusu başlık dosyasında kullanılan işlevlerin her birini doğrudan bildiren, ancak başka bir şey içermemesi gereken başlık dosyalarının her birini doğrudan tanımlayan başlık dosyalarını içermelidir. Bir işaretçi veya C ++ referans türü doğrudan kullanım olarak nitelendirilmez; ileri referanslar tercih edilir.

Boş bir #includedirektif için bir yer var ve bu otomatik bir testte. Bir yazılım paketindeki her başlık dosyası için, aşağıdakileri otomatik olarak oluşturup derlerim:

#include "path/to/random/header_under_test"
int main () { return 0; }

Derleme temiz olmalıdır (örn. Herhangi bir uyarı veya hata olmamalıdır). Eksik veya bilinmeyen türlerle ilgili uyarılar veya hatalar, test edilen başlık dosyasının bazı eksik #includedirektiflere ve / veya eksik ileri bildirimlere sahip olduğu anlamına gelir. İyi not: Sadece testin geçmesi, #includedirektif setinin yeterli olduğu, minimum düzeyde olmadığı anlamına gelmez .


Öyleyse, A adlı bir yapı tanımlayan bir kütüphanem varsa ve B adlı bu kütüphane, bu yapıyı kullanır ve B kütüphanesi C programı tarafından kullanılırsa, K kütüphanesinin A başlık dosyasını B kütüphanesinin ana başlığına eklemeli miyim, yoksa Ben sadece bildirmek isterim? A kütüphanesi derleme sırasında derlenir ve B kütüphanesine bağlanır.
MarcusJ

@MarcusJ - Başlığa dahil olmayanlar başlığı altında listelediğim ilk şey , #include ifadelerinin berbat olmasıydı . B üstbilgisi dosyası, A üstbilgisi dosyasındaki tanımlara bağlı değilse, B üstbilgisi dosyası B üstbilgisi dosyasına # eklemeyin, bir üstbilgi dosyası üçüncü taraf bağımlılıklarını belirtme veya yönergeleri belirleme yeri değildir. Bunlar, bir üst düzey benioku dosyası gibi başka bir yere gider.
David Hammen

1
@MarcusJ - Sorunuzu cevaplamak için cevabımı güncelledim. Sorunuza tek bir cevap olmadığını unutmayın. Birkaç aşırılık ile göstereceğim. Durum 1: B kitaplığının B kitaplığının işlevselliğini doğrudan kullandığı tek yer kitaplık B kaynak dosyalarındadır. Durum 2: Kütüphane, K kütüphanesindeki A işlevselliğinin ince bir uzantısıdır, doğrudan K kütüphanesi için başlık dosyaları / kitaplığı A kütüphanesinde tanımlanan türleri ve / veya işlevleri kullanarak içerir. Kütüphane için başlık (lar). 2 durumunda, bu maruz kalma hemen hemen zorunludur.
David Hammen

Evet, bu durum 2, üzgünüm yorumum, B kütüphanesinde A kütüphanesinde beyan edilen türleri kullandığı gerçeğini atladı. B'nin başlıkları, ilan etmeyi ileriye götürebileceğimi düşünüyordum, ama bunun işe yarayacağını sanmıyorum. Güncelleme için teşekkürler.
Marcus J

Başlık dosyasına sabit eklemek büyük bir hayır-hayır mı?
mding5692

15

Zaten söylenenlere ek olarak.

H dosyaları her zaman içermelidir:

  • Kaynak kodu dokümantasyonu !!! En azından, çeşitli parametrelerin ve fonksiyonların dönüş değerlerinin amacı nedir.
  • Başlık korumaları, #ifndef MYHEADER_H #define MYHEADER_H ... #endif

H dosyaları asla içermemelidir:

  • Her türlü veri tahsisi.
  • İşlev tanımları Satır içi işlevler, bazı durumlarda nadir bir istisna olabilir.
  • Etiketlenmiş herhangi bir şey static.
  • Typedef, uygulamanın geri kalanıyla ilgisi olmayan #fine veya sabitleri tanımlar.

(Sabit olmayan global / harici değişkenleri herhangi bir yerde kullanmak için hiçbir zaman bir neden olmadığını da söyleyebilirim, ama bu başka bir gönderi için bir tartışma.)


1
Neleri vurguladığınız dışında her şeye katılıyorum. Bir kitaplık yapıyorsanız, evet, kitaplığınızı veya kitaplığınızın kullanıcılarını belgelemelisiniz. Bir şirket içi proje için, eğer iyi, kendi kendini açıklayıcı değişken ve işlev adları kullanıyorsanız, başlıklarınızı belgelerle karıştırmanız gerekmez.
martiert

5
@martiert Ayrıca "kodun kendisi için konuşmasına izin ver" okulundanım. Yine de, en azından her zaman işlevlerinizi belgelemelisiniz, kendiniz dışında kimse kullanmasanız bile. Özel ilgi alanları şunlardır: işlevin hata işlemesi durumunda, hangi hata kodlarını döndürür ve hangi koşullar altında başarısız olur? İşlev başarısız olursa parametrelere (tamponlar, işaretçiler vb.) Ne olur? Çok alakalı bir başka şey de: işaretçi parametreleri arayan kişiye bir şey döndürüyor, yani ayrılmış bellek mi bekliyorlar? ->

1
Arayan kişiye işlev içinde hangi hata işlemenin yapıldığı ve ne yapılmadığı açık olmalıdır. Eğer fonksiyon ayrılmış bir tampon beklerse, büyük olasılıkla sınır dışı kontrolleri de arayan kişiye bırakacaktır. İşlev, yürütülecek başka bir işleve dayanıyorsa, bunun belgelenmesi gerekir (örn. Link_list_add () işlevinden önce link_list_init () komutunu çalıştırın). Ve son olarak, fonksiyonun dosya oluşturma, iş parçacığı, zamanlayıcılar ya da her neyse "yan etkisi" varsa, belgelerde belirtilmelidir. ->

1
Belki de "kaynak kod dokümantasyonu" burada çok geniştir, bu gerçekten kaynak koduna aittir. Girdi ve çıktı, ön ve son koşullar ve yan etkileri olan "kullanım dokümantasyonu" kesinlikle oraya gitmeli, epik değil ama kısa bir biçimde.
güvenli

2
Biraz gecikmiş, ancak belgeler için +1. Bu sınıf neden var? Kod yok değil kendisi için konuşur. Bu işlev ne yapar? RTFC (ince .cpp dosyasını oku) müstehcen bir dört harfli kısaltmadır. İnsan asla anlayışı için RTFC'ye mecbur olmamalıdır. Başlıktaki prototip, bazı çıkarılabilir yorumlarda (örneğin, doxygen), argümanların ne olduğunu ve fonksiyonun ne yaptığını özetlemelidir. Bu veri üyesi neden var, içeriyor ve metre, feet veya furlongs cinsinden değeri nedir? Bu da (çıkarılabilir) yorumlar için başka bir konu var.
David Hammen

4

Muhtemelen asla asla söylemem ama, ayrıştırıldıklarında veri ve kod üreten ifadeler bir .h dosyasında olmamalıdır.

Makrolar, satır içi işlevler ve şablonlar veri veya koda benzeyebilir, ancak ayrıştırıldıkları gibi kod üretmezler, bunun yerine kullanılırlarsa. Bu öğelerin genellikle birden fazla .c veya .cpp içinde kullanılması gerekir, bu yüzden .h'ye aittirler.

Benim görüşüme göre, bir başlık dosyası karşılık gelen bir .c veya .cpp için minimum pratik arayüze sahip olmalıdır. Arayüz, #defines, class, typedef, struct tanımları, fonksiyon prototipleri ve global değişkenler için daha az tercih edilen harici tanımları içerebilir. Bununla birlikte, bir bildirim yalnızca bir kaynak dosyada kullanılıyorsa, muhtemelen .h'den dışlanmalı ve bunun yerine kaynak dosyada bulunmalıdır.

Bazıları aynı fikirde olmayabilir, ancak .h dosyaları için kişisel ölçütlerim, derlemeleri için gereken tüm diğer .h dosyalarını içermeleridir. Bazı durumlarda, bu çok fazla dosya olabilir, bu yüzden büyük bir içerme dosyası ağacı olabilecekleri dahil etmeden, işaretçileri bir sınıftaki nesnelere göstericileri kullanmamızı sağlayan sınıflara ileri bildirimler gibi dış bağımlılıkları azaltmak için bazı etkili yöntemlere sahibiz.


3

Başlık dosyasında aşağıdaki kuruluş bulunmalıdır:

  • tip ve sabit tanımlar
  • dış nesne bildirimleri
  • dış işlev bildirimleri

Üstbilgi dosyaları hiçbir zaman nesne tanımları içermemeli, yalnızca tanımları ve nesne bildirimlerini yazmalıdır.


Satır içi fonksiyon tanımları ne olacak?
İstanköy

Satır içi işlevi yalnızca bir C modülünün içinde kullanılan “yardımcı” bir işlevse, yalnızca bu .c dosyasına koyun. Satır içi işlevi iki veya daha fazla modülden görülebiliyorsa, başlık dosyasının içine yerleştirin.
saat

Ayrıca, işlev bir kütüphane sınırında görünür olmak zorundaysa, her şeyi değiştirdiğinizde kütüphaneyi kullanan herkesi derlemeye zorladığından satır içi yapmayın.
Donal Fellows

@DonalFellows: Bu geriye dönük bir çözüm. Daha iyi bir kural: Sık sık değişikliğe tabi olan başlıklara herhangi bir şey koymayın. İşlev faydayı yoksa ve yalnızca temel veri yapısı değiştiğinde değişecek net bir tanım varsa, bir başlıkta kısa bir küçük işlev satırında yanlış bir şey yoktur. İşlev tanımı temel yapı tanımı değiştiği için değişirse, evet, her şeyi yeniden derlemeniz gerekir, ancak yapı tanımı değiştiği için yine de bunu yapmanız gerekir.
David Hammen

0

Ayrıştırıldıkça veri ve kod üreten ifadeler bir .hdosyada olmamalıdır . Benim bakış açım söz konusu olduğunda, bir üstbilgi dosyası sadece ilgili .cveya onun için en az pratik bir arayüze sahip olmalıdır .cpp.

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.