Ini adalah salah satu dari jenis standar pengkodean "harus" daripada "harus". Alasannya adalah Anda harus menulis parser C ++ untuk menjalankannya.
Aturan yang sangat umum untuk file header adalah mereka harus berdiri sendiri. File tajuk tidak boleh mengharuskan beberapa file tajuk lainnya disertakan # sebelum memasukkan tajuk yang dimaksud. Ini adalah persyaratan yang dapat diuji. Diberikan beberapa tajuk acak foo.hh
, yang berikut ini harus dikompilasi dan dijalankan:
#include "foo.hh"
int main () {
return 0;
}
Aturan ini memiliki konsekuensi berkaitan dengan penggunaan kelas lain di beberapa header. Terkadang konsekuensi tersebut dapat dihindari dengan meneruskan mendeklarasikan kelas-kelas lainnya. Ini tidak mungkin dengan banyak kelas perpustakaan standar. Tidak ada cara untuk meneruskan mendeklarasikan contoh template seperti std::string
atau std::vector<SomeType>
. Anda harus memiliki #include
tajuk STL di tajuk meskipun hanya menggunakan jenis ini sebagai argumen untuk fungsi.
Masalah lain adalah dengan hal-hal yang secara tidak sengaja Anda seret. Contoh: pertimbangkan yang berikut:
file foo.cc:
#include "foo.hh"
#include "bar.hh"
void Foo::Foo () : bar() { /* body elided */ }
void Foo::do_something (int item) {
...
bar.add_item (item);
...
}
Berikut bar
adalah Foo
data anggota kelas yang bertipe Bar
. Anda telah melakukan hal yang benar di sini dan telah memasukkan # bar.hh meskipun itu harus dimasukkan dalam header yang mendefinisikan kelas Foo
. Namun, Anda belum memasukkan barang yang digunakan oleh Bar::Bar()
dan Bar::add_item(int)
. Ada banyak kasus di mana panggilan ini dapat menghasilkan referensi eksternal tambahan.
Jika Anda menganalisis foo.o
dengan alat seperti nm
itu, akan tampak bahwa fungsi di foo.cc
memanggil semua jenis hal yang belum Anda lakukan sesuai #include
. Jadi, haruskah Anda menambahkan #include
arahan untuk referensi eksternal insidentil itu foo.cc
? Jawabannya sama sekali tidak. Masalahnya adalah sangat sulit untuk membedakan fungsi-fungsi yang dipanggil secara kebetulan dari yang dipanggil secara langsung.
#include "x.h"
akan bekerja tanpa memerlukan beberapa sebelumnya#include
. Itu cukup baik jika Anda tidak menyalahgunakan#define
.Jika Anda perlu menegakkan aturan bahwa file header tertentu harus berdiri sendiri, Anda dapat menggunakan alat yang sudah Anda miliki. Buat makefile dasar yang mengkompilasi setiap file header secara terpisah tetapi tidak menghasilkan file objek. Anda akan dapat menentukan mode mana untuk mengkompilasi file header dalam (mode C atau C ++) dan memverifikasi bahwa itu bisa berdiri sendiri. Anda dapat membuat asumsi yang masuk akal bahwa output tidak mengandung false positive, semua dependensi yang diperlukan dinyatakan dan output akurat.
Jika Anda menggunakan IDE, Anda mungkin masih dapat melakukannya tanpa makefile (tergantung pada IDE Anda). Cukup buat proyek tambahan, tambahkan file header yang ingin Anda verifikasi dan ubah pengaturan untuk mengkompilasinya sebagai file C atau C ++. Di MSVC misalnya, Anda akan mengubah pengaturan "Jenis Item" di "Properti Konfigurasi-> Umum".
sumber
Saya tidak berpikir alat seperti itu ada, tetapi saya akan senang jika ada jawaban lain yang menyangkal saya.
Masalah dengan menulis alat seperti itu adalah sangat mudah melaporkan hasil yang salah, jadi saya memperkirakan keuntungan bersih dari alat tersebut mendekati nol.
Satu-satunya cara alat semacam itu bisa bekerja adalah jika alat itu dapat mengatur ulang tabel simbolnya menjadi hanya isi file header yang diproses, tetapi kemudian Anda mengalami masalah bahwa header yang membentuk API eksternal perpustakaan mendelegasikan deklarasi aktual untuk tajuk internal.
Misalnya,
<string>
dalam implementasi libc ++ GCC tidak mendeklarasikan apa pun, tetapi hanya menyertakan sekelompok header internal yang berisi deklarasi aktual. Jika alat mereset tabel simbolnya menjadi hanya apa yang dideklarasikan dengan<string>
sendirinya, itu bukan apa-apa.Anda bisa membuat alat membedakan antara
#include ""
dan#include <>
, tetapi itu tidak membantu Anda jika pustaka eksternal menggunakan#include ""
untuk memasukkan tajuk internal dalam API.sumber
tidak
#Pragma once
mencapai ini? Anda dapat memasukkan sesuatu sebanyak yang Anda inginkan, baik secara langsung atau melalui rantainya, dan selama ada di#Pragma once
sebelah masing-masing, tajuk hanya disertakan sekali.Untuk menegakkannya, mungkin Anda bisa membuat sistem build yang hanya menyertakan setiap header dengan sendirinya dengan beberapa fungsi utama dummy, hanya untuk memastikannya dikompilasi.
#ifdef
rantai termasuk untuk hasil terbaik dengan metode pengujian itu.sumber
Selalu sertakan file header dalam file CPP. Ini tidak hanya mempersingkat waktu kompilasi secara signifikan, tetapi juga menghemat banyak masalah jika Anda memutuskan untuk pergi dengan header yang telah dikompilasi. Dalam pengalaman saya, bahkan melakukan masalah deklarasi maju layak saat latihan. Langgar aturan hanya bila perlu.
sumber
Saya akan mengatakan ada kelebihan dan kekurangan dari konvensi ini. Di satu sisi senang mengetahui persis apa yang termasuk dalam file .cpp Anda. Di sisi lain, daftar menyertakan dapat dengan mudah tumbuh ke ukuran yang konyol.
Salah satu cara untuk mendorong konvensi ini adalah tidak memasukkan apa pun di header Anda sendiri, tetapi hanya dalam file .cpp. Maka setiap file .cpp yang menggunakan header Anda tidak akan dikompilasi kecuali Anda secara eksplisit menyertakan semua header lain yang menjadi sandarannya.
Mungkin ada kompromi yang masuk akal di sini. Misalnya, Anda dapat memutuskan bahwa memasukkan header perpustakaan standar ke dalam header Anda boleh saja, tetapi tidak lagi.
sumber