Saya telah melihat bahwa ada beberapa paradigma yang berbeda dalam C ++ tentang apa yang masuk ke file header dan apa ke file cpp. AFAIK, kebanyakan orang, terutama yang berlatar belakang C, melakukan:
foo.h
class foo {
private:
int mem;
int bar();
public:
foo();
foo(const foo&);
foo& operator=(foo);
~foo();
}
foo.cpp
#include foo.h
foo::bar() { return mem; }
foo::foo() { mem = 42; }
foo::foo(const foo& f) { mem = f.mem; }
foo::operator=(foo f) { mem = f.mem; }
foo::~foo() {}
int main(int argc, char *argv[]) { foo f; }
Namun, dosen saya biasanya mengajar C ++ kepada pemula seperti ini:
foo.h
class foo {
private:
int mem;
int bar() { return mem; }
public:
foo() { mem = 42; }
foo(const foo& f) { mem = f.mem; }
foo& operator=(foo f) { mem = f.mem; }
~foo() {}
}
foo.cpp
#include foo.h
int main(int argc, char* argv[]) { foo f; }
// other global helper functions, DLL exports, and whatnot
Awalnya berasal dari Jawa, saya juga selalu terjebak dengan cara kedua ini karena beberapa alasan, seperti bahwa saya hanya perlu mengubah sesuatu di satu tempat jika antarmuka atau nama metode berubah, bahwa saya suka lekukan yang berbeda dari hal-hal di kelas ketika saya lihat implementasinya, dan saya menemukan nama lebih mudah dibaca foo
dibandingkan dengan foo::foo
.
Saya ingin mengumpulkan pro dan kontra untuk kedua cara. Mungkin masih ada cara lain?
Salah satu kelemahan dari cara saya tentu saja adalah kebutuhan untuk deklarasi maju sesekali.
sumber
foo.cpp
sekarang tidak ada hubungannya denganfoo
kelas Anda dan harus dibiarkan kosong (mungkin tetapi#include
untuk membuat sistem build Anda bahagia).Jawaban:
Sementara versi kedua lebih mudah untuk menulis, itu mencampur antarmuka dengan implementasi.
File sumber yang menyertakan file header perlu dikompilasi ulang setiap kali file header diubah. Di versi pertama Anda hanya akan mengubah file header jika Anda perlu mengubah antarmuka. Di versi kedua Anda akan mengubah file header jika Anda perlu mengubah antarmuka atau implementasinya.
Selain itu Anda tidak boleh mengekspos detail implementasi , Anda akan mendapatkan kompilasi ulang yang tidak perlu dengan versi kedua.
sumber
Saya melakukannya dengan cara kedua di tahun '93 -95. Butuh beberapa menit untuk mengkompilasi ulang aplikasi kecil dengan 5-10 fungsi / file (pada PC 486 yang sama .. dan tidak, saya juga tidak tahu tentang kelas, saya baru berusia 14-15 tahun dan tidak ada internet ) .
Jadi, apa yang Anda ajarkan kepada pemula dan apa yang Anda gunakan secara profesional adalah teknik yang sangat berbeda, terutama di C ++.
Saya pikir perbandingan antara C ++ dan mobil F1 sangat tepat. Anda tidak memasukkan pemula ke dalam mobil F1 (yang bahkan tidak dapat dinyalakan kecuali Anda memanaskan mesin hingga 80-95 derajat celcius).
Jangan mengajarkan C ++ sebagai bahasa pertama. Anda harus cukup berpengalaman untuk mengetahui mengapa opsi 2 lebih buruk daripada opsi 1 secara umum, tahu sedikit apa artinya kompilasi / penghubungan statis dan dengan demikian memahami mengapa C ++ lebih memilihnya dengan cara pertama.
sumber
Metode kedua adalah apa yang saya sebut kelas yang sepenuhnya inline. Anda sedang menulis definisi kelas tetapi semua kode Anda menggunakannya hanya akan sebaris kode.
Ya, kompiler memutuskan kapan harus inline dan kapan tidak ... Dalam hal ini Anda membantu kompilator membuat keputusan, dan Anda berpotensi akan menghasilkan kode yang lebih sedikit dan berpotensi lebih cepat.
Keuntungan ini kemungkinan lebih besar daripada fakta bahwa jika Anda memodifikasi implementasi suatu fungsi, Anda perlu membangun kembali semua sumber yang menggunakannya. Dalam kelas yang ringan, Anda tidak akan memodifikasi implementasinya. Jika Anda menambahkan metode baru, Anda harus memodifikasi tajuk.
Ketika kelas Anda menjadi lebih kompleks, meskipun menambahkan loop, manfaat melakukannya dengan cara ini turun.
Itu masih memiliki kelebihan, khususnya:
Kelemahan dari inlining menjadi masalah ketika itu berarti Anda harus memasukkan spesifikasi implementasi ke header Anda, yaitu Anda harus mulai memasukkan header tambahan.
Perhatikan bahwa templat adalah kasus khusus karena Anda harus menyertakan detail implementasi. Anda mungkin mengaburkannya di file lain tetapi harus ada di sana. (Ada pengecualian untuk aturan itu dengan instantiations tetapi secara umum Anda inline template Anda).
sumber
Ini mungkin tidak signifikan, atau benar jika eksekusi Anda menjadi lebih besar, tetapi lebih banyak kode dalam file header memungkinkan kompiler lebih banyak peluang untuk mengoptimalkan kecepatan.
Jika Anda memutuskan untuk menulis pustaka header saja , topik ini hanyalah salah satu perhatian Anda.
sumber