C / C ++ başlık dosyası sırasını içerir


288

Hangi sipariş dosyaları belirtilmelidir, yani bir üstbilgiyi diğerine eklemenin nedenleri nelerdir?

Örneğin, sistem dosyaları, STL ve Boost yerel içerme dosyalarından önce mi sonra mı gidiyor?


2
Ve aşağıdaki cevapların bolluğu, Java geliştiricilerinin neden ayrı başlıklara karşı karar verdikleridir. :-) Bazı gerçekten iyi cevaplar, ancak, özellikle kendi başlık dosyalarının tek başına emin olmak için uyarı.
Chris K

37
100'den fazla oyu olan ve bazı insanlar için son derece ilginç olan soruların "yapıcı değil" olarak nasıl kapandığını seviyorum.
Andreas

Şiddetle tavsiye edilen bir okuma: cplusplus.com/forum/articles/10627
Kalsan

3
Mrt, Nazi topluluğunu ÇOK hatırlatıyor: Ya çok katı kurallara uyuyorsun, ya da "Senin için uygun Cevap / Yorum Yok!". Yine de, birisi herhangi bir programlama ile ilgili bir sorun varsa, bu (genellikle) gidilecek ilk site ..
Imago

Yanıtlar:


289

Derlediği sürece önerilen bir sipariş olduğunu sanmıyorum! Can sıkıcı olan, bazı başlıkların önce diğer başlıkların dahil edilmesini gerektirmesidir ... Bu, başlıkların sırası ile değil başlıkların kendileriyle ilgili bir sorundur.

Kişisel tercihim yerelden küresel olana, her alt bölümü alfabetik sırayla, yani:

  1. Bu cpp dosyasına karşılık gelen h dosyası (varsa)
  2. aynı bileşenden başlıklar,
  3. diğer bileşenlerden başlıklar,
  4. sistem başlıkları.

Benim için #includegerekçem , her başlığın (bir cpp olduğu) önkoşullar olmadan (terminus technicus: header "müstakil" olduğunu) kanıtlaması gerektiğidir . Ve geri kalanlar mantıklı bir şekilde oradan akıyor gibi görünüyor.


16
Globalden lokal olana doğru gitmem dışında, kaynak dosyaya karşılık gelen başlık özel bir işlem görmüyor.
Jon Purdy

127
@Jon: Bunun tam tersi olduğunu söyleyebilirim! :-) Metodunuzun gizli bağımlılıklar getirebileceğini, myclass.cpp <string> sonra <myclass.h> içeriyorsa, myclass.h dosyasının dizeye bağlı olabileceğini düşünmenin bir yolu olmadığını söyleyebilirim; bu nedenle daha sonra sizde veya başka biri myclass.h içeriyorsa ancak dizeye gerek duymuyorsa, cpp'de veya başlığın kendisinde düzeltilmesi gereken bir hata alırsınız. Ama insanların uzun vadede daha iyi çalışacağını düşünüp düşünmediklerini bilmek isterim ... Neden teklifinize bir cevap göndermiyorsunuz ve kimin "kazandığını" göreceğiz? ;-)
33'te squelart

3
Genel sıralamaya özgü, şu anda Dave Abrahams'in tavsiyesinden kullandığım şey. Ve yerel, daha genel olan kaynaklarda yer alan eksik başlıkların aydınlatılmasının aynı nedenini not ediyor. Önemli olan, bu hataları yapma olasılığınızın 3. taraf ve sistem kütüphanelerinden daha fazla olması.
GrafikRobot

7
@PaulJansen Bu kötü bir uygulamadır ve onunla patlaması muhtemel olan bir tekniği kullanmak iyidir, böylece kötü uygulama gizli kalmak yerine düzeltilebilir. yerel - küresel FTW
bames53 09

10
@PaulJansen Evet, standart davranışı geçersiz kılmaktan bahsediyordum. Örneğin, ODR'nin kırılması kazayla olabileceği gibi, kazara da olabilir. Çözüm, bu tür kazalar meydana geldiğinde saklanan uygulamaları kullanmak değil, mümkün olduğunca yüksek sesle havaya uçurmaları muhtemel olan uygulamaları kullanmaktır, böylece hatalar mümkün olduğunca erken farkedilebilir ve düzeltilebilir.
20:13

106

Akılda tutulması gereken en önemli şey, başlıklarınızın önce dahil edilen diğer başlıklara bağlı olmaması gerektiğidir. Bunu sağlamanın bir yolu da başlıklarınızı diğer başlıklardan önce eklemektir.

"C ++ 'da Düşünmek" özellikle bundan bahsediyor ve Lakos'un "Büyük Ölçekli C ++ Yazılım Tasarımına" atıfta bulunuyor:

Bir bileşenin .h dosyasının harici olarak sağlanan bildirimler veya tanımlar olmadan kendi kendine ayrıştırılmasını sağlayarak gizli kullanım hataları önlenebilir. .H dosyasını .c dosyasının ilk satırı olarak dahil etmek, kritik bir parçanın bileşeninin fiziksel arabirimine özgü bilgiler .h dosyasında eksiktir (veya varsa, .c dosyasını derlemeye çalıştığınızda bunu öğreneceksiniz).

Yani, aşağıdaki sırayla ekleyin:

  1. Bu uygulamanın prototip / arabirim başlığı (yani, bu .cpp / .cc dosyasına karşılık gelen .h / .hh dosyası).
  2. Gerektiği gibi aynı projeden diğer başlıklar.
  3. Diğer standart olmayan, sistem dışı kitaplıkların başlıkları (örneğin, Qt, Eigen, vb.).
  4. Diğer "neredeyse standart" kitaplıkların başlıkları (örneğin, Boost)
  5. Standart C ++ başlıkları (örneğin, iostream, fonksiyonel vb.)
  6. Standart C başlıkları (örneğin, cstdint, dirent.h, vb.)

Başlıklardan herhangi birinin bu siparişe dahil edilmeyle ilgili bir sorunu varsa, bunları düzeltin (sizinkini) veya kullanmayın. Temiz başlık yazmayan kütüphaneleri boykot edin.

Google'ın C ++ stil kılavuzu savunuyor neredeyse hiç gerçekten hiçbir gerekçe ile, ters; Şahsen Lakos yaklaşımını destekleme eğilimindeyim.


13
Google C ++ Stil Kılavuzu şu andan itibaren Lakos'un önerisini izleyerek ilgili başlık dosyasını eklemenizi önermektedir.
Filip Bártek

İlk ilgili başlığın çok ötesine geçemezsiniz, çünkü bir kez proje içi başlıklarınızı dahil etmeye başladığınızda bir çok sistem bağımlılığını çekeceksiniz.
Micah

@Micah - "sistem bağımlılıklarının birçoğunu" çeken proje içi başlıklar kötü tasarım, tam da burada kaçınmaya çalıştığımız şey. Mesele, hem gereksiz içeriklerden hem de çözümlenmemiş bağımlılıklardan kaçınmaktır. Tüm başlıklar, önce diğer başlıklar dahil edilmeden eklenebilmelidir. Bir proje içi üstbilginin sistem bağımlılığına ihtiyaç duyması durumunda, öyle olsun - o dosyaya yerel kod bu sistem deposundan bir şeyler kullanmadığı sürece, sistem bağımlılığını bundan sonra eklemezsiniz (ve eklememelisiniz). Kullandığınız sistem bölümlerini dahil etmek için başlıklara (sizinkine bile) güvenemezsiniz.
Nathan Paul Simons

49

Sorunların büyük çoğunluğunu önleyen iki basit kurala uyuyorum:

  1. Tüm üstbilgiler (ve aslında herhangi bir kaynak dosya) ihtiyaç duyduklarını içermelidir. Onlar should not şeylere dahil kullanıcılarını güveniyor.
  2. Ek olarak, tüm üstbilgilerin yukarıdaki kural 1'in aşırı iddialı uygulamasıyla birden fazla kez dahil edilmemeleri için korumaları içermesi gerekir.

Ayrıca aşağıdaki yönergeleri de takip ediyorum:

  1. Önce sistem başlıklarını (stdio.h, vb.) Bir bölme çizgisiyle ekleyin.
  2. Bunları mantıksal olarak gruplandırın.

Diğer bir deyişle:

#include <stdio.h>
#include <string.h>

#include "btree.h"
#include "collect_hash.h"
#include "collect_arraylist.h"
#include "globals.h"

Her ne kadar, rehber olmak, bu öznel bir şeydir. Diğer yandan, kurallar, sıkıca, hatta 'sarıcı' başlık dosyaları korumaları ve gruplandırılmış bazı iğrenç üçüncü taraf geliştirici vizyonum abone olmazsa içerir sağlamak için bile zorlamak :-)


6
+1 "Tüm başlıklar (ve aslında herhangi bir kaynak dosya) ihtiyaç duydukları şeyleri içermelidir. Kullanıcılar da dahil olmak üzere kullanıcılarına güvenmemelidir." Yine de pek çok insan bu örtülü içerme davranışına, örneğin NULL ile güvenir ve <cstddef> içermez. Bu kodu taşımak ve NULL üzerinde derleme hataları almak çalışırken çok sinir bozucu (sadece 0 şimdi kullanmak bir nedeni).
stinky472

20
Neden önce sistem başlıklarını ekliyorsunuz? İlk kuralınız yüzünden diğer neden daha iyi olurdu.
jhasse

Özellik Testi Makroları kullanıyorsanız, ilk eklemeniz büyük olasılıkla standart bir kitaplık başlığı OLMAMALIDIR. Bu nedenle, genel olarak, "önce yerel, sonra küresel" politikasının en iyisi olduğunu söyleyebilirim.
hmijail resignees

1
"Kullanıcılara güvenmeme" konusunda ilk önerinizle ilgili olarak, başlık dosyasında başlık dosyasının eklenmesini gerektirmeyen ileri bildirimlere ne dersiniz? Üstbilgi dosyaları dahil edilmeli mi, çünkü ileri bildirimler uygun dosyaları dahil etmek için üstbilgi dosyasının kullanıcısını dikkate alır.
Zoso

22

Duvara kendi tuğlamı eklemek için.

  1. Her bir başlığın kendi kendine yeterli olması gerekir; bu, yalnızca ilk önce en az bir kez dahil edilmişse test edilebilir
  2. Semboller (makro, türler vb.) Tanıtarak üçüncü taraf üstbilgisinin anlamını yanlışlıkla değiştirmemelisiniz.

Bu yüzden genellikle şöyle giderim:

// myproject/src/example.cpp
#include "myproject/example.h"

#include <algorithm>
#include <set>
#include <vector>

#include <3rdparty/foo.h>
#include <3rdparty/bar.h>

#include "myproject/another.h"
#include "myproject/specific/bla.h"

#include "detail/impl.h"

Her grup bir sonrakinden boş bir satırla ayrılır:

  • Önce bu cpp dosyasına karşılık gelen başlık (akıl sağlığı kontrolü)
  • Sistem başlıkları
  • Bağımlılık sırasına göre düzenlenmiş üçüncü taraf başlıkları
  • Proje başlıkları
  • Proje özel başlıkları

Ayrıca, sistem başlıklarının yanı sıra, her dosyanın ad alanının adıyla bir klasörde olduğunu unutmayın, çünkü bunları bu şekilde izlemek daha kolaydır.


2
Böylece diğer başlık dosyaları bunlardan etkilenmez. Hem bu sistem başlıklarının tanımladığı (hem X içerdiği hem de Windows içerdiği #definediğer kodları bozan şeyler hakkında kötüdür ) ve örtük bağımlılıkları önlemek için. Örneğin, kod tabanı başlık dosyamız foo.hgerçekten bağlıysa <map>ancak .ccdosyalarda kullanıldığı her yerde , <map>zaten dahil edilmişse , muhtemelen fark etmeyeceğiz. Birisi foo.hilk dahil olmadan dahil etmeye çalışana kadar <map>. Ve sonra sinirlenirlerdi.

@ 0A0D: İkinci konu, buradaki sırayla bir sorun değil, çünkü her biri .h birinin .cppönce onu içeren en az bir tane var (gerçekten, kişisel ilişkili Birim testi önce içeriyor ve kaynak kodu bunu doğru grubuna dahil ediyor ). Etkilenmemekle ilgili olarak, başlıklardan herhangi birini içeriyorsa, <map>daha sonra dahil edilen tüm başlıklar yine de etkilenir, bu yüzden bana kaybedilen bir savaş gibi görünüyor.
Matthieu M.Temmuz

1
Tabii ki, bu yüzden düzenli olarak dolaşmak ve sadece derleme süreleri sürücüler çünkü gereksiz bir içerir gerektiren eski kodu (veya daha yeni kodu) düzeltmek.

@MatthieuM. Birincisi senin mantığının ardındaki mantığı bilmek istiyorum Header corresponding to this cpp file first (sanity check). #include "myproject/example.h"Tüm içeriklerin sonuna taşınırsa özel bir şey var mı ?
MNS

1
@MNS: Bir başlık bağımsız olmalıdır, yani başka bir üstbilginin önüne eklenmesine gerek yoktur. Bundan emin olmak, başlığın yazarı olarak sizin sorumluluğunuzdadır ve bunu yapmanın en iyi yolu, ilk önce bu başlığın dahil edildiği bir kaynak dosyaya sahip olmaktır. Başlık dosyasına karşılık gelen kaynak dosyasını kullanmak kolaydır, başka bir iyi seçenek de başlık dosyasına karşılık gelen birim test kaynak dosyasını kullanmaktır, ancak daha az evrenseldir (birim testleri olmayabilir).
Matthieu M.

16

Ben tavsiye ediyorum:

  1. Oluşturduğunuz .cc modülünün başlığı. (Projenizdeki her üstbilginin, projenizdeki diğer üstbilgilere örtülü bağımlılıkları olmamasına yardımcı olur.)
  2. C sistem dosyaları.
  3. C ++ sistem dosyaları.
  4. Platform / OS / diğer başlık dosyaları (örn. Win32, gtk, openGL).
  5. Projenizdeki diğer başlık dosyaları.

Ve elbette, mümkün olan her bölümdeki alfabetik sıra.

#includeBaşlık dosyalarınızda gereksiz durumlardan kaçınmak için her zaman ileri bildirimler kullanın .


+1, ama neden alfabetik? Sizi daha iyi hissettirebilecek bir şey gibi görünüyor, ancak pratik bir yararı yok.
Ben

9
Alfabetik keyfi bir sıralamadır, ancak kolay bir sıralamadır. Alfabetik yapmak zorunda değilsiniz, ancak herkesin tutarlı bir şekilde yapabilmesi için bir miktar sipariş seçmelisiniz. Ben yinelenen önlemek ve birleştirme kolaylaştırır bulduk. Ve yüce metin kullanırsanız, F5 bunları sizin için sıralar.
i_am_jorf

14

Bunun aklı başında dünyanın herhangi bir yerinde tavsiye edilen bir uygulama olmadığından eminim, ancak sisteme aynı uzunluktaki sözcük olarak sıralanan dosya adı uzunluğuna kadar sıralamayı seviyorum. Şöyle ki:

#include <set>
#include <vector>
#include <algorithm>
#include <functional>

Sanırım içerme düzenine bağımlılıktan utanmaktan kaçınmak için diğer kişilerin önüne kendi başlıklarınızı eklemek iyi bir fikirdir.


3
Bu sırayla ikinci, üçüncü ve ilk harften oluşan bir anahtar kullanarak üstbilgilerimi sıralamak istiyorum :-) Yani vektör, set, algoritma, örnek için işlevsel.
paxdiablo

@paxdiablo, bahşiş için teşekkürler. Kullanmayı düşünüyorum, ancak dosya yığınlarının yığınını kararsız ve devrilme olasılığı bırakabileceğinden endişe ediyorum. Bu olursa ne dahil edilebileceğini kim bilir - belki de windows.h.
clstrfsck

40
Sıralama uzunluğu ? Cinnet!
James McNellis

1
Birincisi için +1. Aslında mantıklıdır, bir dosyanın içindeki başlıkları gözlerinizle görsel olarak bulmanız gerekiyorsa, alfabetikten çok daha iyidir.
Kugel

6

Bu öznel değil. Başlıklarınızın #includebelirli bir sırada d olmasına bağlı olmadığından emin olun . STL veya Boost başlıklarını hangi sırayla eklediğinizin önemli olmadığından emin olabilirsiniz.


1
Örtük bağımlılıklar olmadığını varsayıyordum
Anycorn

Evet, ancak derleyici bu varsayımı yapamaz, bu nedenle #include <A>, <B>, derleninceye kadar asla #include <B>, <A> ile aynı değildir.
Mikhail

4

Öncelikle .cpp ... 'e karşılık gelen üstbilgiyi, başka bir şey eklemeden önce source1.cppiçermelidir source1.h. Aklıma gelen tek istisna, MSVC'yi önceden derlenmiş başlıklarla kullanırken, bu durumda stdafx.hbaşka bir şeyden önce dahil etmek zorunda kalırsınız .

Muhakeme: dahil source1.ho 's bağımlılıkları olmadan tek başına var olamayacağını başka dosyalardan önce garantiler. Daha source1.hsonraki bir tarihte bir bağımlılık alırsanız , derleyici derhal gerekli ileri bildirimleri eklemeniz için sizi uyaracaktır source1.h. Bu da başlıkların bağımlıları tarafından herhangi bir sıraya dahil edilmesini sağlar.

Misal:

source1.h

class Class1 {
    Class2 c2;    // a dependency which has not been forward declared
};

source1.cpp

#include "source1.h"    // now compiler will alert you saying that Class2 is undefined
                    // so you can forward declare Class2 within source1.h
...

MSVC kullanıcıları: Önceden derlenmiş başlıklar kullanmanızı önemle tavsiye ederim. Bu nedenle, #includestandart başlıklar (ve asla değişmeyecek diğer başlıklar) için tüm yönergeleri taşıyın stdafx.h.


2

Varsa, .cpp için karşılık gelen .hpp ile başlayarak en spesifik olandan en az spesifik olana kadar ekleyin. Bu şekilde, başlık dosyalarındaki kendi kendine yeterli olmayan gizli bağımlılıklar ortaya çıkar.

Bu, önceden derlenmiş başlıkların kullanımı ile karmaşıktır. Bunun bir yolu, projenizi derleyiciye özgü yapmadan, proje başlıklarından birini önceden derlenmiş başlık içerme dosyası olarak kullanmaktır.


1

C / C ++ dünyasında zor bir sorudur ve standardın ötesinde pek çok unsuru vardır.

Bence başlık dosya sırası derliyordu gibi derliyorum ciddi bir sorun değildir, dedi.

Benim düşüncelerim: Tüm bu başlıklarda sembol çakışması yoksa, herhangi bir sipariş tamamdır ve başlık bağımlılığı sorunu daha sonra kusurlu .h'ye #include satırları eklenerek düzeltilebilir.

Gerçek zorluk, bazı üstbilgiler yukarıdaki başlıklara göre (#if koşullarını kontrol ederek) eylemini değiştirdiğinde ortaya çıkar.

Örneğin, VS2005'teki stddef.h dosyasında:

#ifdef  _WIN64
#define offsetof(s,m)   (size_t)( (ptrdiff_t)&(((s *)0)->m) )
#else
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif

Şimdi sorun: offsetofKendi sistem başlıklarında sağlamayan bazı eski derleyiciler de dahil olmak üzere birçok derleyici ile kullanılması gereken özel bir üstbilgi ("custom.h") varsa, üstbilgime yazmalıyım:

#ifndef offsetof
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif

Ve kullanıcıya tüm sistem başlıklarından #include "custom.h" sonraoffsetof bahsettiğinizden emin olun , aksi takdirde stddef.h satırı bir makro yeniden tanımlama hatası verir.

Kariyerimizde bu tür olaylarla daha fazla karşılaşmamak için dua ediyoruz.

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.