Di C, Anda tidak dapat memiliki definisi fungsi / implementasi di dalam file header. Namun, di C ++ Anda dapat memiliki implementasi metode lengkap di dalam file header. Mengapa perilakunya berbeda?
Di C, jika Anda menentukan fungsi dalam file header, maka fungsi itu akan muncul di setiap modul yang dikompilasi yang menyertakan file header itu, dan simbol publik akan diekspor untuk fungsi tersebut. Jadi jika fungsi additup didefinisikan dalam header.h, dan foo.c dan bar.c keduanya menyertakan header.h, maka foo.o dan bar.o keduanya akan menyertakan salinan additup.
Ketika Anda pergi untuk menautkan kedua file objek tersebut secara bersamaan, linker akan melihat bahwa penambahan simbol didefinisikan lebih dari sekali, dan tidak akan mengizinkannya.
Jika Anda mendeklarasikan fungsi sebagai statis, maka tidak ada simbol yang akan diekspor. File objek foo.o dan bar.o akan tetap berisi salinan terpisah dari kode untuk fungsi tersebut, dan mereka akan dapat menggunakannya, tetapi linker tidak akan dapat melihat salinan fungsi apa pun, sehingga tidak akan mengeluh. Tentu saja, tidak ada modul lain yang dapat melihat fungsinya. Dan program Anda akan penuh dengan dua salinan identik dengan fungsi yang sama.
Jika Anda hanya mendeklarasikan fungsi dalam file header, tetapi tidak mendefinisikannya, dan kemudian mendefinisikannya hanya dalam satu modul, maka linker akan melihat satu salinan dari fungsi, dan setiap modul dalam program Anda akan dapat melihatnya dan Gunakan. Dan program Anda yang dikompilasi akan berisi hanya satu salinan fungsi.
Jadi, Anda dapat memiliki definisi fungsi di file header di C, itu hanya gaya buruk, bentuk buruk, dan semua ide buruk.
(Dengan "menyatakan", maksud saya menyediakan prototipe fungsi tanpa badan; dengan "mendefinisikan" Maksud saya menyediakan kode aktual dari badan fungsi; ini adalah terminologi standar C.)
#ifndef HEADER_H
adalah apa yang seharusnya dicegah?C dan C ++ berperilaku sangat mirip dalam hal ini - Anda dapat memiliki
inline
fungsi dalam header. Dalam C ++, metode apa pun yang tubuhnya berada di dalam definisi kelas secara implisitinline
. Jika Anda ingin melakukan hal yang sama dalam C, nyatakan fungsinyastatic inline
.sumber
static inline
" ... dan Anda masih akan memiliki banyak salinan fungsi di setiap unit terjemahan yang menggunakannya. Dalam C ++ dengan tidakstatic
inline
berfungsi Anda hanya akan memiliki satu salinan. Untuk benar-benar memiliki implementasi di header di C, Anda harus 1) menandai implementasi sebagaiinline
(misalnyainline void func(){do_something();}
), dan 2) benar-benar mengatakan bahwa fungsi ini akan berada di beberapa unit terjemahan tertentu (misalnyavoid func();
).Konsep file header perlu sedikit penjelasan:
Entah Anda memberikan file pada baris perintah kompiler, atau melakukan '#include'. Sebagian besar kompiler menerima file perintah dengan ekstensi c, C, cpp, c ++, dll. Sebagai file sumber. Namun, mereka biasanya menyertakan opsi baris perintah untuk memungkinkan penggunaan ekstensi sembarang ke file sumber juga.
Umumnya file yang diberikan pada baris perintah disebut 'Sumber', dan yang disertakan disebut 'Header'.
Langkah preprocessor benar-benar mengambil semuanya dan membuat semuanya tampak seperti satu file besar ke kompiler. Apa yang ada di header atau di sumber sebenarnya tidak relevan pada saat ini. Biasanya ada opsi kompiler yang dapat menunjukkan output dari tahap ini.
Jadi untuk setiap file yang diberikan pada baris perintah kompiler, file besar diberikan kepada kompiler. Ini mungkin memiliki kode / data yang akan menempati memori dan / atau membuat simbol untuk dirujuk dari file lain. Sekarang masing-masing akan menghasilkan gambar 'objek'. Linker dapat memberikan 'simbol duplikat' jika simbol yang sama ditemukan di lebih dari dua file objek yang sedang dihubungkan bersama. Mungkin ini alasannya; tidak disarankan untuk meletakkan kode di file header, yang dapat membuat simbol di file objek.
'Inline' biasanya inline .. tetapi ketika debugging mereka mungkin tidak inline. Jadi mengapa linker tidak memberikan kesalahan yang didefinisikan multiply? Sederhana ... Ini adalah simbol 'lemah', dan selama semua data / kode untuk simbol lemah dari semua objek memiliki ukuran dan konten yang sama, tautan tersebut akan menyimpan satu salinan dan menjatuhkan salinan dari objek lain. Berhasil.
sumber
Anda dapat melakukan ini di C99:
inline
fungsi dijamin disediakan di tempat lain, jadi jika suatu fungsi tidak diuraikan, definisinya diterjemahkan ke dalam deklarasi (yaitu, implementasi dibuang). Dan, tentu saja, bisa Anda gunakanstatic
.sumber
C ++ kutipan standar
The C ++ 17 N4659 standar rancangan 10.1.6 "The inline specifier" mengatakan bahwa metode yang secara implisit inline:
dan kemudian lebih jauh ke bawah kita melihat bahwa metode sebaris tidak hanya bisa, tetapi harus didefinisikan pada semua unit terjemahan:
Ini juga secara eksplisit disebutkan dalam catatan di 12.2.1 "Fungsi anggota":
Implementasi GCC 8.3
main.cpp
Kompilasi dan lihat simbol:
keluaran:
kemudian kita melihat
man nm
bahwaMyClass::myMethod
simbol ditandai sebagai lemah pada file objek ELF, yang menyiratkan bahwa itu dapat muncul pada beberapa file objek:sumber
Mungkin karena alasan yang sama bahwa Anda harus meletakkan implementasi metode lengkap di dalam definisi kelas di Jawa.
Mereka mungkin terlihat serupa, dengan kurung berlekuk-lekuk dan banyak kata kunci yang sama, tetapi mereka bahasa yang berbeda.
sumber