.H dosyasına ne girmeli?


95

Kodunuzu birden çok dosyaya bölerken .h dosyasına tam olarak ne girmeli ve .cpp dosyasına ne girmelidir?



7
Bu saf bir stil sorunu, ancak C ++ bildirimlerinin bir .hppdosyaya gittiğine ve C bildirimleri bir .hdosyaya girdiğine inanıyorum . Bu, C ve C ++ kodunu karıştırırken çok faydalıdır (örneğin C'deki eski modüller).
Thomas Matthews

@ThomasMatthews mantıklı. Bu uygulama sıkça kullanılıyor mu?
ty

@lightningleaf: Evet, uygulama özellikle C ++ ve C dillerini karıştırırken sıklıkla kullanılır.
Thomas Matthews

Yanıtlar:


116

Üstbilgi dosyaları ( .h), birden çok dosyada ihtiyaç duyulacak bilgileri sağlamak için tasarlanmıştır. Sınıf bildirimleri, işlev prototipleri ve numaralandırmalar gibi şeyler genellikle başlık dosyalarında bulunur. Tek kelimeyle "tanımlar".

Kod dosyaları ( .cpp), yalnızca tek bir dosyada bilinmesi gereken uygulama bilgilerini sağlamak için tasarlanmıştır. Genel olarak, diğer modüller tarafından erişilmesi gereken / erişilmeyecek olan fonksiyon gövdeleri ve dahili değişkenler, .cppdosyalara aittir . Tek kelimeyle, "uygulamalar".

Neyin nereye ait olduğunu belirlemek için kendinize sormanız gereken en basit soru, "Bunu değiştirirsem, şeylerin yeniden derlenmesini sağlamak için diğer dosyalardaki kodu değiştirmem gerekecek mi?" Cevap "evet" ise, muhtemelen başlık dosyasına aittir; cevap "hayır" ise, muhtemelen kod dosyasına aittir.


4
Özel sınıf verilerinin dışında başlığa gitmesi gerekir. Şablonlar tamamen başlık tanımlı olmalıdır (destekleyen birkaç derleyiciden birini kullanmadığınız sürece export). 1 numarayı bulmanın tek yolu PIMPL'dir. # 2 export, destekleniyorsa ve c ++ 0x ve externşablonlar kullanılarak mümkün olabilir . IMO, c ++ 'daki başlık dosyaları kullanışlılıklarının çoğunu kaybeder.
KitsuneYMG

24
Hepsi iyi, ancak yanlış terminolojiyle. Tek kelimeyle, "bildirimler" - "tanım" terimi "uygulama" ile eş anlamlıdır. Yalnızca bildirimsel kod, satır içi kod, makro tanımları ve şablon kodu bir başlıkta olmalıdır; yani, kodu veya veriyi örnekleyen hiçbir şey.
Clifford

8
Clifford ile aynı fikirdeyim. Beyan ve tanım terimlerini oldukça gevşek ve biraz birbiriyle değiştirilebilecek şekilde kullanıyorsunuz. Ancak C ++ 'da kesin anlamları vardır. Örnekler: Bir sınıf bildirimi, bir sınıfın adını tanıtır ancak içinde ne olduğunu söylemez. Bir sınıf tanımı, tüm üyeleri ve arkadaş işlevlerini listeler. Her ikisi de sorunsuz bir şekilde başlık dosyalarına yerleştirilebilir. "İşlev prototipi" dediğiniz şey bir işlev bildirimidir . Ancak bir işlev tanımı , işlevin kodunu içeren ve bir cpp dosyasına yerleştirilmesi gereken şeydir - satır içi veya bir şablonun parçası olmadığı sürece.
sellibitze

5
C ++ 'da kesin anlamları vardır, İngilizcede kesin anlamları yoktur. Cevabım ikincisinde yazılmıştır.
Amber

56

Gerçek şu ki, C ++ 'da bu, C başlığı / kaynak organizasyonundan biraz daha karmaşıktır.

Derleyici ne görüyor?

Derleyici, başlıkları düzgün şekilde dahil edilmiş büyük bir kaynak (.cpp) dosyası görür. Kaynak dosya, bir nesne dosyasına derlenecek derleme birimidir.

Öyleyse, başlıklar neden gerekli?

Çünkü bir derleme birimi, başka bir derleme birimindeki bir uygulama hakkında bilgiye ihtiyaç duyabilir. Dolayısıyla, örneğin bir işlevin bir kaynakta gerçekleştirilmesi yazılabilir ve bu işlevin bildirimi, onu kullanmak isteyen başka bir kaynakta yazılabilir.

Bu durumda, aynı bilginin iki kopyası vardır. Hangisi kötü ...

Çözüm, bazı ayrıntıları paylaşmaktır. Uygulamanın Kaynakta kalması gerekirken, işlevler gibi paylaşılan sembollerin beyanı veya yapıların, sınıfların, numaralandırmaların vb. Paylaşılması gerekebilir.

Başlıklar, bu paylaşılan ayrıntıları koymak için kullanılır.

Başlığa, birden çok kaynak arasında nelerin paylaşılması gerektiğine dair bildirimleri taşıyın

Daha fazlası yok mu?

C ++ 'da, başlığa eklenebilecek başka şeyler de vardır çünkü bunların da paylaşılması gerekir:

  • satır içi kod
  • şablonlar
  • sabitler (genellikle anahtarların içinde kullanmak istedikleriniz ...)

Paylaşılan uygulamalar da dahil olmak üzere paylaşılması gereken HER ŞEYi başlığa taşıyın

Bu, başlıkların içinde kaynaklar olabileceği anlamına mı geliyor?

Evet. Aslında, bir "başlık" içinde olabilecek (yani kaynaklar arasında paylaşılan) pek çok farklı şey vardır.

  • İleri beyanlar
  • bildirimler / işlevlerin / yapıların / sınıfların / şablonların tanımı
  • satır içi ve şablonlu kodun uygulanması

Karmaşık hale gelir ve bazı durumlarda (semboller arasındaki döngüsel bağımlılıklar) tek başlıkta tutulması imkansız hale gelir.

Başlıklar üç bölüme ayrılabilir

Bu, aşırı bir durumda şunlara sahip olabileceğiniz anlamına gelir:

  • ileri bildirim başlığı
  • bir bildirim / tanım başlığı
  • bir uygulama başlığı
  • bir uygulama kaynağı

Bir MyObject şablonumuz olduğunu hayal edelim. Sahip olabilirdik:

// - - - - MyObject_forward.hpp - - - - 
// This header is included by the code which need to know MyObject
// does exist, but nothing more.
template<typename T>
class MyObject ;

.

// - - - - MyObject_declaration.hpp - - - - 
// This header is included by the code which need to know how
// MyObject is defined, but nothing more.
#include <MyObject_forward.hpp>

template<typename T>
class MyObject
{
   public :
      MyObject() ;
   // Etc.
} ;

void doSomething() ;

.

// - - - - MyObject_implementation.hpp - - - - 
// This header is included by the code which need to see
// the implementation of the methods/functions of MyObject,
// but nothing more.
#include <MyObject_declaration.hpp>

template<typename T>
MyObject<T>::MyObject()
{
   doSomething() ;
}

// etc.

.

// - - - - MyObject_source.cpp - - - - 
// This source will have implementation that does not need to
// be shared, which, for templated code, usually means nothing...
#include <MyObject_implementation.hpp>

void doSomething()
{
   // etc.
} ;

// etc.

Vaov!

"Gerçek hayatta", genellikle daha az karmaşıktır. Çoğu kodun, kaynakta bazı satır içi kodlarla birlikte yalnızca basit bir başlığı / kaynak kuruluşu olacaktır.

Ancak diğer durumlarda (birbirini tanıyan şablonlu nesneler), bazı derleme hatalarını görmeme yardımcı olmak için her nesne için ayrı bildirim ve uygulama başlıklarına sahip olmak zorunda kaldım, bu başlıkları içeren boş bir kaynak.

Başlıkları ayrı başlıklara bölmenin bir başka nedeni, derlemeyi hızlandırmak, gerekli olan katı ile ayrıştırılan simgelerin miktarını sınırlamak ve bir satır içi yöntem uygulaması değiştiğinde yalnızca ileri bildirime önem veren bir kaynağın gereksiz yeniden derlenmesinden kaçınmak olabilir.

Sonuç

Kod organizasyonunuzu hem olabildiğince basit hem de olabildiğince modüler yapmalısınız. Kaynak dosyaya mümkün olduğunca çok koyun. Yalnızca paylaşılması gerekenleri başlıklarda gösterin.

Ancak şablonlu nesneler arasında döngüsel bağımlılıklarınız olacağı gün, kod organizasyonunuz, düz başlık / kaynak organizasyonundan biraz daha "ilginç" hale gelirse şaşırmayın ...

^ _ ^


18

Diğer tüm yanıtlara ek olarak, size bir başlık dosyasına ne yerleştirmeyeceğinizi söyleyeceğim:
usingbildirim (en yaygın olanı using namespace std;), dahil olduğu kaynak dosyanın ad alanını kirlettiği için bir başlık dosyasında görünmemelidir. .


Bazı ayrıntılı ad alanında (veya anonim bir ad alanında) olduğu sürece kullanabileceğiniz bir uyarı ile +1. Ama evet, usingbir başlıkta küresel isim alanına bir şeyler getirmek için asla kullanmayın .
KitsuneYMG

+1 Bunu yanıtlamak çok daha kolay. :) Ayrıca başlık dosyaları olmalıdır değil ihtiva anonim ad alanlarını.
sellibitze

Başlık dosyalarının, bunun ne anlama geldiğini anladığınız sürece, yani her çeviri biriminin ad alanını tanımladığınız şeylerin farklı bir kopyasına sahip olacağı sürece, anonim ad alanları içermesi uygundur. Anonim ad alanlarındaki satır içi işlevler, static inlineiç bağlantıyı şablonlarla birleştirdiğinizde olanlarla ilgili bir şey olması nedeniyle C99'da kullandığınız durumlar için C ++ 'da önerilir . Anon ad alanları, harici bağlantıyı korurken işlevleri "gizlemenize" izin verir.
Steve Jessop

Steve, yazdıkların beni ikna etmedi. Lütfen başlık dosyasında anon ad alanının tamamen anlamlı olduğunu düşündüğünüz somut bir örnek seçin.
sellibitze

7

Ne hiçliğe derler (sıfır ikili ayak izi) başlık dosyasına girer.

Değişkenler hiçbir şeyde derlenmez, ancak tür bildirimleri yapar (çünkü yalnızca değişkenlerin nasıl davrandığını açıklarlar).

işlevler yoktur, ancak satır içi işlevler yapar (veya makrolar), çünkü yalnızca çağrıldığında kod üretirler.

şablonlar kod değildir, sadece kod oluşturmak için bir reçetedir. böylece h dosyalarına da girerler.


1
"satır içi işlevler ... yalnızca çağrıldığı yerde kod üretir". Bu doğru değil. satır içi işlevler çağrı sitelerinde satır içi olabilir veya olmayabilir, ancak satır içi olsalar bile, gerçek işlev gövdesi, satır içi olmayan bir işlevde olduğu gibi hala mevcuttur. Üstbilgilerde satır içi işlevlerin olmasının normal olmasının nedeni, kod oluşturup oluşturmadıklarıyla ilgili değildir, çünkü satır içi işlevler tek bir tanım kuralını tetiklemez, bu nedenle satır içi olmayan işlevlerden farklı olarak iki farklı çeviri birimini birbirine bağlamada sorun yoktur her ikisi de başlığı içeriyor.
Steve Jessop

4

Genel olarak, bildirimleri başlık dosyasına ve tanımları uygulama (.cpp) dosyasına koyarsınız. Bunun istisnası, tanımın başlıkta da yer alması gereken şablonlardır.

Bu soru ve buna benzer sorular SO'da sıkça soruldu - bkz. Neden C ++ başlık dosyaları ve .cpp dosyaları var? ve C ++ Üstbilgi Dosyaları, örneğin Kod Ayrımı .


Tabii ki, sınıf tanımlarını başlık dosyalarına da koyabilirsiniz . Şablon olmak zorunda bile değiller.
sellibitze

2

Sınıf ve işlev bildirimleriniz artı belgeler ve satır içi işlevler / yöntemler için tanımlar (bazıları bunları ayrı .inl dosyalarına koymayı tercih etse de).


2

Temelde başlık dosyası, sınıf iskeleti veya bildirimi içerir (sık sık değişmez)

ve cpp dosyası sınıf uygulamasını içerir (sık sık değişir).


5
Lütfen standart olmayan terminoloji kullanmaktan kaçının. "Sınıf iskeleti" nedir, "sınıf uygulaması" nedir? Ayrıca, sınıflar bağlamında bildirim dediğiniz şey muhtemelen sınıf tanımlarını içerir.
sellibitze

1

başlık dosyası (.h) sınıfların, yapıların ve yöntemlerinin, prototiplerinin vb. bildirimleri için olmalıdır. Bu nesnelerin gerçeklenmesi cpp'de yapılır.

.h içinde

    class Foo {
    int j;

    Foo();
    Foo(int)
    void DoSomething();
}

1

Görmeyi bekliyorum:

  • beyannameler
  • yorumlar
  • satır içi olarak işaretlenmiş tanımlar
  • şablonlar

gerçekten cevap, ne koymamak gerektiğidir:

  • tanımlar (şeylerin çarpılarak tanımlanmasına yol açabilir)
  • bildirimler / yönergeler kullanarak (bunları başlığınız dahil herhangi bir kişiye zorlar, isim çarpmalarına neden olabilir)

1
Sınıf tanımlarını kesinlikle başlık dosyalarına da koyabilirsiniz . Bir sınıf bildirimi üyeleri hakkında hiçbir şey söylemez.
sellibitze

1

Başlık bir şeyi tanımlar ancak uygulama hakkında hiçbir şey söylemez. (Bu "metafor" daki Şablonlar hariçtir.

Bununla birlikte, "tanımları" alt gruplara ayırmanız gerekir, bu durumda iki tür tanım vardır.

  • Yapınızın "düzenini", yalnızca çevreleyen kullanım gruplarının ihtiyaç duyduğu kadarını söyleyerek tanımlarsınız.
  • Bir değişken, fonksiyon ve bir sınıfın tanımları.

Şimdi, tabii ki birinci alt gruptan bahsediyorum.

Başlık, yazılımın geri kalanının uygulamayı kullanmasına yardımcı olmak için yapınızın düzenini tanımlamak için oradadır. Bunu uygulamanızın bir "soyutlaması" olarak görmek isteyebilirsiniz, ki şiddetle söylendiği gibi, ancak bu durumda oldukça uygun olduğunu düşünüyorum.

Önceki posterlerin söylediği ve gösterdiği gibi, özel ve genel kullanım alanlarını ve bunların başlıklarını beyan ettiğinizi söylediğiniz gibi, bu ayrıca özel ve genel değişkenleri içerir. Şimdi, burada kodun tasarımına girmek istemiyorum, ancak başlıklarınıza ne koyduğunuzu düşünmek isteyebilirsiniz, çünkü bu son kullanıcı ile uygulama arasındaki Katmandır.


1
  • Başlık dosyaları - geliştirme sırasında çok sık değişmemelidir -> düşünmeli ve bir kerede yazmalısınız (ideal durumda)
  • Kaynak dosyalar - uygulama sırasındaki değişiklikler

Bu bir uygulamadır. Bazı küçük projeler için gitmenin yolu bu olabilir. Ancak, imzalarını değiştirmek veya kaldırmak yerine işlevleri ve prototiplerini (başlık dosyalarında) kullanımdan kaldırmayı deneyebilirsiniz. En azından ana numarayı değiştirene kadar. 1.9.2'nin 2.0.0 beta sürümüne yükseltilmesi gibi.
TamusJRoyce

1

Üstbilgi (.h)

  • Arayüzler için gerekli makrolar ve dahil (mümkün olduğunca az)
  • Fonksiyonların ve sınıfların beyanı
  • Arayüzün dokümantasyonu
  • Varsa, satır içi işlevlerin / yöntemlerin beyanı
  • genel değişkenlere harici (varsa)

Gövde (.cpp)

  • Geri kalan makrolar ve şunları içerir
  • Modülün başlığını ekleyin
  • Fonksiyonların ve yöntemlerin tanımı
  • Global değişkenler (varsa)

Genel bir kural olarak, modülün "paylaşılan" kısmını .h'ye (diğer modüllerin görebilmesi gereken kısım) ve "paylaşılmayan" kısmını .cpp'ye koyarsınız.

PD: Evet, global değişkenleri ekledim. Bazı zamanlar kullandım ve başlıklarda tanımlamamak önemli, yoksa her biri kendi değişkenini tanımlayan çok sayıda modül elde edeceksiniz.

DÜZENLEME: David'in yorumundan sonra değiştirildi


Genel bir kural olarak, mümkün olduğunca az içerme .h dosyasında olmalıdır ve .cpp dosyası, ihtiyaç duyduğu başlıkları içermelidir. Bu, derleme sürelerini kısaltır ve ad alanlarını kirletmez.
David Thornley
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.