Deklarasi ke depan vs. sertakan

17

Reduce the number of #include files in header files. It will reduce build times. Instead, put include files in source code files and use forward declarations in header files.

Saya membaca ini di sini. http://www.yolinux.com/TUTORIALS/LinuxTutorialC++CodingStyle.html .

Jadi dikatakan jika kelas (kelas A) dalam file header tidak perlu menggunakan definisi sebenarnya dari beberapa kelas (kelas B). Pada saat itu kita dapat menggunakan deklarasi maju alih-alih termasuk file header (kelas B) tertentu.

Pertanyaan: Jika kelas (kelas A) di header jika tidak menggunakan definisi aktual dari kelas tertentu (kelas B), lalu bagaimana deklarasi maju membantu mengurangi waktu kompilasi?

Nayana Adassuriya
sumber

Jawaban:

11

Kompilator tidak peduli jika kelas A menggunakan kelas B. Ia hanya tahu bahwa ketika kelas A dikompilasi dan tidak memiliki deklarasi kelas B sebelumnya (deklarasi maju atau sebaliknya), ia panik dan menandainya sebagai kesalahan.

Yang penting di sini adalah bahwa kompiler tahu Anda tidak mencoba mengkompilasi program setelah kucing Anda berjalan di keyboard Anda dan membuat beberapa huruf acak yang mungkin atau mungkin tidak ditafsirkan sebagai kelas.

Ketika melihat menyertakan, untuk dapat menggunakan informasi yang terkandung di dalamnya, ia harus membuka file dan menguraikannya (terlepas dari apakah itu benar-benar perlu atau tidak). Jika file itu kemudian menyertakan file lain, itu juga harus dibuka dan diuraikan, dll. Jika ini bisa dihindari, umumnya merupakan ide yang baik untuk menggunakan deklarasi maju sebagai gantinya.

Sunting : Pengecualian aturan ini dikompilasi tajuk. Dalam hal ini, semua tajuk dikompilasi dan disimpan untuk kompilasi di masa mendatang. Jika tajuk tidak berubah, kompiler dapat dengan cerdas menggunakan tajuk yang dikompilasi dari kompilasi sebelumnya dan dengan demikian mengurangi waktu kompilasi, tetapi itu hanya berfungsi dengan baik jika Anda tidak sering perlu mengganti tajuk.

Neil
sumber
Terima kasih atas penjelasannya. Kemudian ok seperti contoh Anda berpikir ada tiga file header vehicle.h, bus.h, toybus.h. vehicle.htermasuk oleh bus.hdan bus.htermasuk oleh toybus.h. jadi jika saya melakukan beberapa perubahan bus.h. apakah kompiler terbuka dan mem-parsing vehicle.hlagi? apakah itu mengkompilasinya lagi?
Nayana Adassuriya
1
@NayanaAdassuriya Ya, itu akan disertakan dan diurai setiap kali, yang juga mengapa Anda melihat #pragma onceatau #ifndef __VEHICLE_H_mengetik deklarasi dalam file header untuk mencegah file seperti itu dimasukkan beberapa kali (atau digunakan beberapa kali setidaknya dalam kasus ifndef).
Neil
4

karena itu A.hpp tidak perlu # mencantumkan B.hpp

jadi A.hpp menjadi

class B;//or however forward decl works for classes

class A
{
    B* bInstance_;
//...
}

jadi ketika A.hpp dimasukkan maka B.hpp tidak disertakan secara implisit dan semua file yang hanya bergantung pada A.hpp tidak perlu dikompilasi ulang setiap kali perubahan b.hpp

ratchet freak
sumber
tetapi dalam file sumber (A.cpp). perlu menyertakan file header aktual (Bh). Jadi setiap kali perlu dikompilasi. Akhirnya kedua cara Bh perlu mengkompilasi ulang dengan perubahan. Ada yang berbeda?
Nayana Adassuriya
@NayanaAdassuriya no karena A hanya menggunakan pointer ke B dan perubahan ke B tidak akan mempengaruhi A.hpp (atau file yang memasukkannya)
ratchet freak
@NayanaAdassuriya: Ya, A.cpp harus mengkompilasi ulang (jika menggunakan definisi B di dalam tubuh metode A, tetapi biasanya demikian), tetapi C.cpp, yang menggunakan A, tetapi tidak B secara langsung, tidak akan.
Jan Hudec
3

Ingat, C / C ++ preprocessor adalah langkah pemrosesan terpisah, murni tekstual. The #includetarikan direktif dalam isi header disertakan dan compiler harus mengurai itu. Selain itu, kompilasi masing-masing .cppbenar-benar terpisah, sehingga fakta bahwa kompiler hanya diuraikan B.hketika mengkompilasi B.cpptidak membantunya ketika dibutuhkan lagi ketika mengkompilasi A.cpp. Dan lagi saat kompilasi C.cpp. Dan D.cpp. Dan seterusnya. Dan masing-masing file tersebut harus dikompilasi ulang jika ada file yang termasuk di dalamnya telah berubah.

Jadi katakanlah kelas Amenggunakan kelas Bdan kelas Cdan Dmenggunakan kelas A, tetapi tidak perlu memanipulasi B. Jika kelas Adapat dideklarasikan hanya dengan deklarasi maju B, daripada B.hdikompilasi dua kali: ketika mengkompilasi B.cppdan A.cpp(karena Bmasih diperlukan di dalam Ametode).

Tapi ketika A.hmeliputi B.h, dikompilasi empat kali-saat kompilasi B.cpp, A.cpp, C.cppdan D.cppsebagai kemudian dua sekarang secara tidak langsung termasuk B.hjuga.

Juga ketika tajuk dimasukkan lebih dari sekali, preprocessor masih harus membacanya setiap kali. Ini akan melewati pemrosesan kontennya karena #ifdefpelindung, tetapi masih membacanya dan perlu mencari ujung penjaga, yang berarti harus menguraikan semua arahan preprosesor di dalam.

(Seperti yang disebutkan dalam jawaban lain, header yang dikompilasi berusaha untuk mengatasi hal ini, tetapi mereka adalah kaleng cacing mereka sendiri; pada dasarnya Anda dapat menggunakannya secara wajar untuk header sistem dan hanya jika Anda tidak menggunakan terlalu banyak dari mereka, tetapi tidak untuk header di proyek Anda)

Jan Hudec
sumber
+1, tajuk-header hanya menjadi masalah serius ketika Anda memiliki jumlah kelas yang cukup besar, bukan ketika hanya memiliki dua kelas A dan B. Semua pos lainnya sepertinya ketinggalan titik pusat itu.
Doc Brown
2

Deklarasi penerusan jauh lebih cepat untuk diuraikan daripada seluruh file tajuk yang dengan sendirinya mungkin menyertakan lebih banyak file tajuk.

Juga, jika Anda mengubah sesuatu di file header untuk kelas B, segala sesuatu termasuk header itu harus dikompilasi ulang. Dengan deklarasi maju, itu mungkin hanya file sumber tempat implementasi A berada. Tetapi jika header A benar-benar termasuk header B, semuanya termasuk a.hppakan dikompilasi ulang juga, bahkan jika itu tidak menggunakan apa pun dari B.

Benjamin Kloster
sumber