C / C ++ termasuk urutan file header


287

Urutan apa yang harus dimasukkan file yang ditentukan, yaitu apa alasan untuk menyertakan satu header sebelum yang lain?

Misalnya, apakah file sistem, STL, dan Boost berjalan sebelum atau setelah file include lokal?


2
Dan sebagian besar jawaban di bawah ini adalah alasan mengapa pengembang Java memutuskan untuk tidak memilih header yang berbeda. :-) Beberapa jawaban yang sangat bagus, khususnya, peringatan untuk memastikan file header Anda dapat berdiri sendiri.
Chris K

37
Saya suka bagaimana pertanyaan yang memiliki 100+ suara dan sangat menarik bagi sebagian orang ditutup sebagai "tidak konstruktif".
Andreas

Bacaan yang sangat direkomendasikan: cplusplus.com/forum/articles/10627
Kalsan

3
@mrt, SO sangat mengingatkan komunitas sup Nazi: Entah Anda mengikuti beberapa aturan yang sangat ketat, atau "Tidak Ada Jawaban / Komentar yang tepat untuk Anda!". Meskipun demikian, jika seseorang memiliki masalah yang berkaitan dengan cara pemrograman, ini adalah (biasanya) situs pertama yang pergi ..
Imago

Jawaban:


289

Saya tidak berpikir ada pesanan yang disarankan, asalkan dikompilasi! Yang menjengkelkan adalah ketika beberapa header membutuhkan header lain untuk dimasukkan terlebih dahulu ... Itu masalah dengan header itu sendiri, bukan dengan urutan menyertakan.

Preferensi pribadi saya adalah beralih dari lokal ke global, masing-masing subbagian dalam urutan abjad, yaitu:

  1. file h yang terkait dengan file cpp ini (jika berlaku)
  2. header dari komponen yang sama,
  3. header dari komponen lain,
  4. header sistem.

Alasan saya untuk 1. adalah bahwa itu harus membuktikan bahwa setiap header (yang ada cpp) dapat #included tanpa prasyarat (terminus technicus: header adalah "mandiri"). Dan sisanya sepertinya mengalir secara logis dari sana.


16
Sama seperti Anda, kecuali saya beralih dari global ke lokal, dan header yang sesuai dengan file sumber tidak menerima perlakuan khusus.
Jon Purdy

127
@ Jon: Saya akan mengatakan itu justru sebaliknya! :-) Saya berpendapat bahwa metode Anda dapat memperkenalkan dependensi tersembunyi, katakanlah jika myclass.cpp menyertakan <string> lalu <myclass.h>, tidak ada cara untuk mengetahui pada waktu build bahwa myclass.h sendiri dapat bergantung pada string; jadi jika nanti Anda, atau orang lain, termasuk myclass.h tetapi tidak perlu string, Anda akan mendapatkan kesalahan yang perlu diperbaiki baik di cpp atau di header itu sendiri. Tapi saya akan tertarik untuk mengetahui apakah itu yang orang pikir akan bekerja lebih baik dalam jangka panjang ... Mengapa Anda tidak mengirim jawaban dengan proposal Anda dan kita akan melihat siapa yang "menang"? ;-)
squelart

3
Khusus untuk pemesanan umum adalah apa yang saya gunakan saat ini dari rekomendasi Dave Abrahams. Dan dia mencatat alasan yang sama dengan @squelart untuk menerangi header yang hilang termasuk dalam sumber, dari lokal ke yang lebih umum. Kuncinya adalah Anda lebih mungkin melakukan kesalahan daripada pihak ke-3, dan pustaka sistem.
GrafikRobot

7
@ PaulJansen Itu adalah praktik yang buruk dan ada baiknya menggunakan teknik yang lebih mungkin meledak dengan itu sehingga praktik yang buruk dapat diperbaiki daripada meletakkan tersembunyi. lokal ke FTW global
bames53

10
@ PaulJansen Ya, saya merujuk pada mengesampingkan perilaku standar. Itu bisa terjadi secara tidak sengaja seperti, misalnya, melanggar ODR dapat terjadi secara tidak sengaja. Solusinya bukan menggunakan praktik yang bersembunyi ketika kecelakaan seperti itu terjadi, tetapi menggunakan praktik yang paling mungkin membuat mereka meledak sekeras mungkin, sehingga kesalahan dapat diperhatikan dan diperbaiki sedini mungkin.
bames53

106

Hal besar yang perlu diingat adalah bahwa tajuk Anda tidak boleh bergantung pada tajuk lainnya yang dimasukkan terlebih dahulu. Salah satu cara untuk memastikan ini adalah dengan memasukkan header Anda sebelum header lainnya.

"Berpikir dalam C ++" secara khusus menyebutkan ini, merujuk pada Lakos '"Desain Perangkat Lunak C ++ Skala Besar":

Kesalahan penggunaan laten dapat dihindari dengan memastikan bahwa file .h komponen diurai dengan sendirinya - tanpa deklarasi atau definisi yang disediakan secara eksternal ... Termasuk file .h sebagai baris pertama dari file .c memastikan bahwa tidak ada bagian penting informasi intrinsik ke antarmuka fisik komponen hilang dari file .h (atau, jika ada, Anda akan mengetahuinya segera setelah Anda mencoba untuk mengkompilasi file .c).

Artinya, termasuk dalam urutan berikut:

  1. Header prototipe / antarmuka untuk implementasi ini (yaitu, file .h / .hh yang sesuai dengan file .cpp / .cc ini).
  2. Header lain dari proyek yang sama, sesuai kebutuhan.
  3. Header dari pustaka non-sistem, non-standar lainnya (misalnya, Qt, Eigen, dll).
  4. Header dari perpustakaan "hampir-standar" lainnya (misalnya, Boost)
  5. Header C ++ standar (misalnya, iostream, fungsional, dll.)
  6. Header C standar (misalnya, cstdint, dirent.h, dll.)

Jika ada header yang memiliki masalah untuk dimasukkan dalam pesanan ini, perbaiki (atau milik Anda) atau jangan gunakan. Boikot perpustakaan yang tidak menulis tajuk bersih.

Panduan gaya C ++ Google berargumen hampir secara terbalik, dengan benar-benar tidak ada pembenaran sama sekali; Saya pribadi cenderung menyukai pendekatan Lakos.


13
Sampai sekarang, Google C ++ Style Guide merekomendasikan untuk memasukkan file header terkait terlebih dahulu, mengikuti saran Lakos.
Filip Bártek

Anda tidak mendapatkan banyak hal di luar tajuk terkait pertama karena begitu Anda mulai memasukkan tajuk dalam proyek Anda, Anda akan menarik banyak ketergantungan sistem.
Micah

@Micah - header dalam proyek yang menarik "banyak ketergantungan sistem" adalah desain yang buruk, tepatnya yang kami coba hindari di sini. Intinya adalah untuk menghindari dependensi yang tidak perlu dan dependensi yang tidak terselesaikan. Semua header harus dapat dimasukkan tanpa menyertakan header lainnya terlebih dahulu. Dalam kasus di mana header dalam proyek membutuhkan ketergantungan sistem, maka jadilah itu - maka Anda tidak (dan tidak seharusnya) memasukkan ketergantungan sistem setelahnya, kecuali kode lokal untuk file tersebut menggunakan hal-hal dari dep sistem itu. Anda tidak dapat dan tidak harus bergantung pada tajuk (termasuk punyamu) untuk menyertakan deps sistem yang Anda gunakan.
Nathan Paul Simons

49

Saya mengikuti dua aturan sederhana yang menghindari sebagian besar masalah:

  1. Semua header (dan memang semua file sumber) harus menyertakan apa yang mereka butuhkan. Mereka seharusnya tidak mengandalkan pengguna mereka termasuk hal-hal.
  2. Sebagai tambahan, semua header harus menyertakan penjaga sehingga mereka tidak dimasukkan beberapa kali oleh penerapan aturan 1 di atas yang terlalu ambisius.

Saya juga mengikuti pedoman:

  1. Sertakan tajuk sistem terlebih dahulu (stdio.h, dll) dengan garis pemisah.
  2. Kelompokkan secara logis.

Dengan kata lain:

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

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

Meskipun, sebagai pedoman, itu adalah hal subjektif. Peraturan di sisi lain, saya tegakkan dengan kaku, bahkan sampai memberikan file header 'wrapper' dengan mencakup penjaga dan dikelompokkan termasuk jika beberapa pengembang pihak ketiga yang menjengkelkan tidak berlangganan visi saya :-)


6
+1 "Semua header (dan memang file sumber apa pun) harus menyertakan apa yang mereka butuhkan. Mereka tidak boleh bergantung pada pengguna mereka termasuk hal-hal." Namun begitu banyak orang mengandalkan perilaku inklusi implisit ini, misalnya, dengan NULL dan tidak menyertakan <cstddef>. Ini sangat menjengkelkan ketika mencoba untuk porting kode ini dan mendapatkan kesalahan kompilasi pada NULL (satu alasan saya hanya menggunakan 0 sekarang).
stinky472

20
Mengapa Anda memasukkan header sistem terlebih dahulu? Akan lebih baik yang lain mengapa ada karena aturan pertama Anda.
jhasse

Jika Anda menggunakan Feature Test Macros, sertakan pertama Anda yang paling mungkin BUKAN menjadi header perpustakaan standar. Karena itu, secara umum, saya akan mengatakan kebijakan "lokal dulu, global nanti" adalah yang terbaik.
hmijail meratapi orang-orang yang mengundurkan diri

1
Mengenai saran pertama Anda untuk "tidak mengandalkan pengguna", bagaimana dengan meneruskan deklarasi dalam file header yang tidak perlu menyertakan file header? Haruskah kita masih memasukkan file header karena deklarasi maju meletakkan tanggung jawab pada pengguna file header untuk memasukkan file yang sesuai.
Zoso

22

Untuk menambahkan batu bata saya sendiri ke dinding.

  1. Setiap tajuk perlu swasembada, yang hanya dapat diuji jika dimasukkan terlebih dahulu setidaknya sekali
  2. Seseorang seharusnya tidak salah memodifikasi arti header pihak ketiga dengan memperkenalkan simbol (makro, tipe, dll.)

Jadi saya biasanya pergi seperti ini:

// 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"

Setiap grup dipisahkan oleh garis kosong dari yang berikutnya:

  • Header terkait dengan file cpp ini terlebih dahulu (cek kewarasan)
  • Header sistem
  • Header pihak ketiga, diorganisasikan berdasarkan urutan ketergantungan
  • Header proyek
  • Proyeksikan tajuk pribadi

Juga perhatikan bahwa, selain dari header sistem, setiap file ada dalam folder dengan nama namespace-nya, hanya karena lebih mudah untuk melacaknya dengan cara ini.


2
Sehingga file header lainnya tidak terpengaruh olehnya. Baik dengan apa yang didefinisikan header sistem (baik X termasuk dan Windows termasuk buruk tentang #define's yang mengacaukan kode lain) dan untuk mencegah dependensi implisit. Sebagai contoh, jika file header basis kode kita foo.hbenar-benar bergantung pada <map>tetapi di mana-mana itu digunakan dalam .ccfile, <map>kebetulan sudah termasuk, kita mungkin tidak akan melihat. Sampai seseorang mencoba memasukkan foo.htanpa terlebih dahulu memasukkan <map>. Dan kemudian mereka akan jengkel.

@ 0A0D: Masalah kedua bukan masalah dalam urutan di sini, karena masing-masing .hmemiliki paling tidak satu .cppyang memasukkannya terlebih dahulu (memang, dalam kode pribadi saya tes Unit terkait menyertakannya terlebih dahulu, dan kode sumber memasukkannya dalam kelompok yang sah ). Mengenai tidak terpengaruh, jika ada header yang termasuk <map>maka semua header yang dimasukkan setelahnya dipengaruhi, jadi sepertinya kalah perang bagi saya.
Matthieu M.

1
Tentu saja, itulah sebabnya saya secara teratur berkeliling dan memperbaiki kode yang lebih lama (atau bahkan kode yang lebih baru) yang memerlukan menyertakan tidak perlu karena itu hanya menaikkan kali membangun.

@ MatthieuM. Saya ingin tahu alasan di balik poin Anda yaitu Header corresponding to this cpp file first (sanity check). Apakah ada sesuatu yang khusus jika #include "myproject/example.h"dipindahkan ke akhir semua termasuk?
MNS

1
@MNS: Header harus berdiri sendiri, artinya, tidak harus menyertakan header lainnya sebelum itu. Merupakan tanggung jawab Anda, sebagai penulis tajuk, untuk memastikan hal ini, dan cara terbaik untuk melakukannya adalah memiliki satu file sumber tempat tajuk ini dimasukkan terlebih dahulu. Menggunakan file sumber yang sesuai dengan file header mudah, pilihan lain yang baik adalah menggunakan file sumber tes unit yang sesuai dengan file header tetapi kurang universal (mungkin tidak ada tes unit).
Matthieu M.

16

Saya merekomendasi:

  1. Header untuk modul .cc yang Anda buat. (Membantu memastikan setiap tajuk di proyek Anda tidak memiliki ketergantungan tersirat pada tajuk lain di proyek Anda.)
  2. File sistem C.
  3. File sistem C ++.
  4. Platform / OS / file header lainnya (mis. Win32, gtk, openGL).
  5. File header lainnya dari proyek Anda.

Dan tentu saja, urutan abjad dalam setiap bagian, jika memungkinkan.

Selalu gunakan deklarasi maju untuk menghindari yang tidak perlu #includedalam file header Anda.


+1, tapi mengapa menurut abjad? Sepertinya sesuatu yang dapat membuat Anda merasa lebih baik, tetapi tidak memiliki manfaat praktis.
Ben

9
Alfabet adalah pemesanan acak, tetapi mudah. Anda tidak harus melakukan abjad, tetapi Anda harus memilih beberapa pemesanan sehingga semua orang melakukannya secara konsisten. Saya menemukan ini membantu menghindari duplikat dan membuat penggabungan lebih mudah. Dan jika Anda menggunakan teks luhur, F5 akan memesannya untuk Anda.
i_am_jorf

14

Saya cukup yakin ini bukan praktik yang direkomendasikan di mana pun di dunia waras, tapi saya suka sistem line termasuk oleh panjang nama file, diurutkan secara leksikal dalam panjang yang sama. Seperti itu:

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

Saya pikir itu ide yang baik untuk memasukkan header Anda sendiri di depan orang lain, untuk menghindari rasa malu dari ketergantungan keteraturan.


3
Saya suka mengurutkan tajuk dengan menggunakan kunci yang terdiri dari huruf kedua, ketiga dan pertama dalam urutan itu :-) Jadi vektor, set, algoritma, fungsional untuk contoh Anda.
paxdiablo

@ paxdiablo, terima kasih atas tipnya. Saya sedang mempertimbangkan untuk menggunakannya, tetapi saya khawatir itu mungkin berakhir meninggalkan tumpukan nama file tidak stabil dan cenderung terbalik. Siapa yang tahu apa yang mungkin dimasukkan jika ini terjadi - bahkan mungkin windows.h.
clstrfsck

40
Diurutkan berdasarkan panjang ? Penyakit jiwa!
James McNellis

1
+1 untuk yang pertama. Masuk akal sebenarnya, jika Anda perlu mencari header secara visual dalam file dengan mata Anda, itu jauh lebih baik daripada abjad.
Kugel

6

Ini tidak subyektif. Pastikan tajuk Anda tidak bergantung pada #included dalam urutan tertentu. Anda dapat yakin tidak masalah urutan apa yang Anda sertakan STL atau Boost header.


1
Saya berasumsi tidak ada dependensi implisit
Anycorn

Ya, tetapi kompiler tidak dapat membuat asumsi ini, jadi #sertakan <A>, <B> tidak pernah sama dengan #sertakan <B>, <A> sampai mereka dikompilasi.
Mikhail

4

Pertama-tama sertakan tajuk yang sesuai dengan .cpp ... dengan kata lain, source1.cppharus termasuk source1.hsebelum memasukkan hal lain. Satu-satunya pengecualian yang dapat saya pikirkan adalah ketika menggunakan MSVC dengan header yang telah dikompilasi dalam hal ini, Anda dipaksa untuk memasukkan stdafx.hsebelum hal lain.

Alasan: Termasuk source1.hsebelum file lain memastikan bahwa itu dapat berdiri sendiri tanpa ketergantungan itu. Jika source1.hmenggunakan dependensi di kemudian hari, kompiler akan segera memperingatkan Anda untuk menambahkan deklarasi penerusan yang diperlukan source1.h. Ini pada gilirannya memastikan bahwa tajuk dapat dimasukkan dalam urutan apa pun oleh tanggungan mereka.

Contoh:

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
...

Pengguna MSVC: Saya sangat merekomendasikan menggunakan header yang telah dikompilasi sebelumnya. Jadi, pindahkan semua #includearahan untuk tajuk standar (dan tajuk lainnya yang tidak akan pernah berubah) ke stdafx.h.


2

Sertakan dari yang paling spesifik ke yang paling spesifik, mulai dengan .hpp yang sesuai untuk .cpp, jika ada. Dengan begitu, setiap dependensi tersembunyi dalam file header yang tidak mencukupi akan terungkap.

Ini rumit dengan penggunaan header yang dikompilasi sebelumnya. Salah satu cara mengatasi hal ini adalah, tanpa membuat kompiler proyek Anda spesifik, adalah dengan menggunakan salah satu header proyek sebagai header termasuk file yang dikompilasi.


1

Ini adalah pertanyaan sulit di dunia C / C ++, dengan begitu banyak elemen di luar standar.

Saya pikir urutan file header bukan masalah serius selama dikompilasi, seperti kata squelart.

Gagasan saya adalah: Jika tidak ada konflik simbol di semua header itu, urutan apa pun OK, dan masalah ketergantungan header dapat diperbaiki nanti dengan menambahkan #include baris ke cacat .h.

Kerumitan sebenarnya muncul ketika beberapa tajuk mengubah aksinya (dengan memeriksa kondisi #jika) sesuai dengan tajuk di atas.

Misalnya, di stddef.h di VS2005, ada:

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

Sekarang masalahnya: Jika saya memiliki header khusus ("custom.h") yang perlu digunakan dengan banyak kompiler, termasuk beberapa yang lebih tua yang tidak menyediakan offsetofheader sistem mereka, saya harus menulis di header saya:

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

Dan pastikan untuk memberi tahu pengguna #include "custom.h" setelah semua header sistem, jika tidak, baris offsetofdi stddef.h akan menyatakan kesalahan redefinisi makro.

Kami berdoa agar tidak bertemu lagi dengan kasus-kasus seperti itu dalam karier kami.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.