Saya sedang membersihkan yang termasuk dalam proyek C ++ yang sedang saya kerjakan, dan saya terus bertanya-tanya apakah saya harus secara eksplisit menyertakan semua header yang digunakan secara langsung dalam file tertentu, atau apakah saya hanya harus memasukkan minimum yang kosong.
Berikut ini contohnya Entity.hpp
:
#include "RenderObject.hpp"
#include "Texture.hpp"
struct Entity {
Texture texture;
RenderObject render();
}
(Mari kita asumsikan bahwa deklarasi maju untuk RenderObject
bukan pilihan.)
Sekarang, saya tahu itu RenderObject.hpp
termasuk Texture.hpp
- Saya tahu itu karena masing-masing RenderObject
memiliki Texture
anggota. Namun, saya secara eksplisit termasuk Texture.hpp
dalam Entity.hpp
, karena saya tidak yakin apakah itu ide yang baik untuk mengandalkan itu termasuk dalam RenderObject.hpp
.
Jadi: Apakah ini praktik yang baik atau tidak?
#ifndef _RENDER_H #define _RENDER_H ... #endif
.#pragma once
menyelesaikannya, bukan?Jawaban:
Anda harus selalu menyertakan semua tajuk yang mendefinisikan objek apa pun yang digunakan dalam file .cpp dalam file tersebut terlepas dari apa yang Anda ketahui tentang apa yang ada di file-file itu. Anda harus menyertakan penjaga di semua file header untuk memastikan bahwa memasukkan header beberapa kali tidak masalah.
Alasan:
Texture
objek dalam file ini.RenderObject.hpp
sebenarnya tidak membutuhkannyaTexture.hpp
sendiri.Yang wajar adalah bahwa Anda tidak boleh memasukkan header di header lain kecuali jika diperlukan secara eksplisit dalam file itu.
sumber
Aturan umum adalah: termasuk apa yang Anda gunakan. Jika Anda menggunakan objek secara langsung, maka sertakan file headernya secara langsung. Jika Anda menggunakan objek A yang menggunakan B tetapi tidak menggunakan B sendiri, hanya sertakan Ah
Juga saat kita sedang membahas topik tersebut, Anda hanya perlu memasukkan file header lainnya dalam file header Anda jika Anda benar-benar membutuhkannya di header. Jika Anda hanya membutuhkannya di .cpp, maka hanya sertakan di sana: ini adalah perbedaan antara ketergantungan publik dan pribadi, dan akan mencegah pengguna kelas Anda dari menyeret header yang sebenarnya tidak mereka butuhkan.
sumber
Iya nih.
Anda tidak pernah tahu kapan tajuk lainnya mungkin berubah. Masuk akal di dunia untuk menyertakan, di setiap unit terjemahan, tajuk yang Anda tahu membutuhkan unit terjemahan.
Kami memiliki pelindung tajuk untuk memastikan bahwa penyertaan ganda tidak berbahaya.
sumber
Pendapat berbeda mengenai hal ini, tetapi saya berpendapat bahwa setiap file (apakah file sumber c / cpp, atau file header h / hpp) harus dapat dikompilasi atau dianalisis sendiri.
Dengan demikian, semua file harus menyertakan # semua dan semua file header yang mereka butuhkan - Anda tidak boleh berasumsi bahwa satu file header sudah termasuk sebelumnya.
Ini sangat menyebalkan jika Anda perlu menambahkan file header dan menemukan bahwa itu menggunakan item yang didefinisikan di tempat lain, tanpa secara langsung memasukkannya ... jadi Anda harus mencari (dan mungkin berakhir dengan yang salah!)
Di sisi lain, itu tidak (sebagai aturan umum) tidak masalah jika Anda # memasukkan file yang tidak Anda butuhkan ...
Sebagai titik gaya pribadi, saya mengatur file #include dalam urutan abjad, dibagi menjadi sistem dan aplikasi - ini membantu memperkuat pesan "mandiri dan sepenuhnya koheren".
sumber
Itu tergantung pada apakah inklusi transitif itu karena kebutuhan (misalnya kelas dasar) atau karena detail implementasi (anggota pribadi).
Untuk memperjelas, inklusi transitif diperlukan saat menghapusnya hanya dapat dilakukan setelah terlebih dahulu mengubah antarmuka yang dinyatakan dalam header perantara. Karena itu sudah merupakan perubahan yang melanggar, semua file .cpp yang menggunakannya harus tetap diperiksa.
Contoh: Ah dimasukkan oleh Bh yang digunakan oleh C.cpp. Jika Bh menggunakan Ah untuk beberapa detail implementasi, maka C.cpp tidak boleh berasumsi bahwa Bh akan terus melakukannya. Tetapi jika Bh menggunakan Ah untuk kelas dasar, maka C.cpp dapat mengasumsikan bahwa Bh akan terus menyertakan header yang relevan untuk kelas dasarnya.
Anda lihat di sini keuntungan sebenarnya dari TIDAK menduplikasi inklusi tajuk. Katakanlah bahwa kelas dasar yang digunakan oleh Bh benar-benar tidak termasuk dalam Ah dan di refactored ke dalam Bh itu sendiri. Bh sekarang menjadi tajuk mandiri. Jika C.cpp secara berlebihan menyertakan Ah, sekarang termasuk header yang tidak perlu.
sumber
Mungkin ada kasus lain: Anda memiliki Ah, Bh dan C.cpp Anda, Bh termasuk Ah
jadi di C.cpp, Anda bisa menulis
Jadi, jika Anda tidak menulis #termasuk "Ah" di sini, apa yang bisa terjadi? dalam C.cpp Anda, baik A dan B (misalnya kelas) digunakan. Kemudian Anda mengubah kode C.cpp Anda, menghapus hal-hal yang terkait B, tetapi membiarkan Bh disertakan di sana.
Jika Anda memasukkan Ah dan Bh dan sekarang pada titik ini, alat yang mendeteksi tidak perlu menyertakan dapat membantu Anda untuk menunjukkan bahwa Bh sertakan tidak lagi diperlukan. Jika Anda hanya menyertakan Bh seperti di atas, maka sulit bagi alat / manusia untuk mendeteksi menyertakan yang tidak perlu setelah perubahan kode Anda.
sumber
Saya mengambil pendekatan yang sedikit berbeda dari jawaban yang diajukan.
Di header, selalu sertakan hanya minimum, apa yang dibutuhkan untuk membuat kompilasi lulus. Gunakan deklarasi maju sedapat mungkin.
Dalam file sumber, tidak terlalu penting seberapa banyak Anda memasukkan. Preferensi saya masih termasuk minimum untuk membuatnya lulus.
Untuk proyek kecil, termasuk tajuk di sana-sini tidak akan membuat perbedaan. Tetapi untuk proyek menengah hingga besar, itu bisa menjadi masalah. Bahkan jika perangkat keras terbaru digunakan untuk mengkompilasi, perbedaannya dapat terlihat. Alasannya adalah bahwa kompilator masih harus membuka header yang disertakan dan menguraikannya. Jadi, untuk mengoptimalkan build, terapkan teknik di atas (termasuk minimal, dan gunakan forward declare).
Meskipun agak ketinggalan zaman, Desain Perangkat Lunak C ++ Skala Besar (oleh John Lakos) menjelaskan semua ini secara terperinci.
sumber
Praktik yang baik adalah jangan khawatir tentang strategi header Anda selama dikompilasi.
Bagian tajuk dari kode Anda hanyalah satu blok garis yang tak seorang pun harus melihatnya sampai Anda mendapatkan kesalahan kompilasi yang mudah diselesaikan. Saya mengerti keinginan untuk gaya yang 'benar', tetapi tidak ada cara yang dapat benar-benar digambarkan sebagai benar. Memasukkan header untuk setiap kelas lebih cenderung menyebabkan kesalahan kompilasi berbasis pesanan yang mengganggu, tetapi kesalahan kompilasi tersebut juga mencerminkan masalah yang dapat diperbaiki oleh pengkodean yang hati-hati (meskipun mereka tidak sepadan dengan waktu untuk memperbaikinya).
Dan ya, Anda akan memiliki masalah berbasis pesanan setelah mulai masuk ke
friend
daratan.Anda dapat memikirkan masalahnya dalam dua kasus.
Kasus 1: Anda memiliki sejumlah kecil kelas yang saling berinteraksi, katakanlah kurang dari selusin. Anda secara teratur menambah, menghapus dari, dan memodifikasi tajuk ini, dengan cara yang dapat memengaruhi ketergantungan mereka satu sama lain. Ini adalah kasus yang disarankan oleh contoh kode Anda.
Kumpulan tajuk cukup kecil sehingga tidak rumit untuk menyelesaikan masalah apa pun yang muncul. Setiap masalah sulit diperbaiki dengan menulis ulang satu atau dua header. Khawatir tentang strategi tajuk Anda adalah menyelesaikan masalah yang tidak ada.
Kasus 2: Anda memiliki banyak kelas. Beberapa kelas mewakili tulang punggung program Anda, dan menulis ulang tajuknya akan memaksa Anda untuk menulis ulang / mengkompilasi ulang sejumlah besar basis kode Anda. Kelas-kelas lain menggunakan tulang punggung ini untuk menyelesaikan banyak hal. Ini mewakili pengaturan bisnis yang khas. Header tersebar di seluruh direktori dan Anda tidak dapat secara realistis mengingat nama-nama segalanya.
Solusi: Pada titik ini, Anda perlu memikirkan kelas Anda dalam kelompok logis dan mengumpulkan kelompok-kelompok itu menjadi header yang menghentikan Anda dari keharusan untuk
#include
berulang-ulang. Ini tidak hanya membuat hidup lebih sederhana, itu juga merupakan langkah yang diperlukan untuk mengambil keuntungan dari header yang telah dikompilasi .Anda berakhir di
#include
kelas yang tidak Anda butuhkan tetapi siapa yang peduli ?Dalam hal ini, kode Anda akan terlihat seperti ...
sumber