Mengapa saya tidak harus menyertakan file cpp dan sebagai gantinya menggunakan header?

147

Jadi saya menyelesaikan tugas pemrograman C ++ pertama saya dan menerima nilai saya. Tetapi menurut penilaian, saya kehilangan nilai untuk including cpp files instead of compiling and linking them. Saya tidak terlalu jelas tentang apa artinya itu.

Melihat kembali kode saya, saya memilih untuk tidak membuat file header untuk kelas saya, tetapi melakukan segalanya dalam file cpp (sepertinya berfungsi dengan baik tanpa file header ...). Saya menduga bahwa grader berarti saya menulis '#include "mycppfile.cpp";' di beberapa file saya.

Alasan saya untuk #includemembuat file cpp adalah: - Segala sesuatu yang seharusnya masuk ke file header ada di file cpp saya, jadi saya berpura-pura itu seperti file header - Di monyet-lihat-monyet lakukan mode, saya melihat yang lain file header ada #includedi file, jadi saya melakukan hal yang sama untuk file cpp saya.

Jadi apa yang sebenarnya saya lakukan salah, dan mengapa itu buruk?

ialm
sumber
36
Ini pertanyaan yang sangat bagus. Saya berharap banyak c ++ pemula akan terbantu dengan ini.
Mia Clarke

Jawaban:

175

Sepengetahuan saya, standar C ++ tidak mengenal perbedaan antara file header dan file sumber. Sejauh menyangkut bahasa, file teks apa pun dengan kode hukum sama dengan yang lain. Namun, meskipun tidak ilegal, termasuk file sumber ke dalam program Anda akan menghilangkan banyak keuntungan yang Anda dapatkan dari memisahkan file sumber Anda.

Intinya, apa yang #includedilakukan adalah memerintahkan preprosesor untuk mengambil seluruh file yang Anda tentukan, dan menyalinnya ke file aktif Anda sebelum kompiler mengambilnya. Jadi ketika Anda memasukkan semua file sumber dalam proyek Anda bersama-sama, pada dasarnya tidak ada perbedaan antara apa yang telah Anda lakukan, dan hanya membuat satu file sumber besar tanpa pemisahan sama sekali.

"Oh, itu bukan masalah besar. Jika berjalan, tidak apa-apa," aku mendengar kamu menangis. Dan dalam arti tertentu, Anda benar. Tetapi saat ini Anda sedang berhadapan dengan program kecil mungil, dan CPU yang bagus dan relatif tidak terbebani untuk mengkompilasi untuk Anda. Anda tidak akan selalu seberuntung itu.

Jika Anda mempelajari bidang pemrograman komputer yang serius, Anda akan melihat proyek dengan jumlah garis yang dapat mencapai jutaan, bukan puluhan. Itu banyak baris. Dan jika Anda mencoba untuk mengkompilasi salah satu dari ini di komputer desktop modern, ini bisa memakan waktu beberapa jam, bukan detik.

"Oh, tidak! Kedengarannya mengerikan! Namun bisakah aku mencegah nasib buruk ini ?!" Sayangnya, tidak banyak yang dapat Anda lakukan tentang itu. Jika dibutuhkan waktu berjam-jam untuk dikompilasi, dibutuhkan waktu berjam-jam untuk dikompilasi. Tapi itu hanya benar-benar penting pertama kali - setelah Anda mengkompilasinya sekali, tidak ada alasan untuk mengkompilasinya lagi.

Kecuali Anda mengubah sesuatu.

Sekarang, jika Anda memiliki dua juta baris kode digabungkan menjadi satu raksasa raksasa, dan perlu melakukan perbaikan bug sederhana seperti, katakanlah x = y + 1, itu berarti Anda harus mengkompilasi semua dua juta baris lagi untuk menguji ini. Dan jika Anda mengetahui bahwa Anda bermaksud melakukan x = y - 1sebaliknya, sekali lagi, dua juta baris kompilasi sedang menunggu Anda. Itu menghabiskan berjam-jam waktu yang bisa lebih baik dihabiskan untuk melakukan hal lain.

"Tapi aku benci menjadi tidak produktif! Kalau saja ada cara untuk menyusun bagian-bagian berbeda dari basis kode saya secara individual, dan entah bagaimana menghubungkannya bersama setelah itu!" Ide bagus, secara teori. Tetapi bagaimana jika program Anda perlu tahu apa yang terjadi dalam file yang berbeda? Tidak mungkin untuk sepenuhnya memisahkan basis kode Anda kecuali jika Anda ingin menjalankan banyak file .exe kecil.

"Tapi pastinya itu mungkin! Pemrograman kedengarannya seperti siksaan murni jika tidak! Bagaimana jika saya menemukan cara untuk memisahkan antarmuka dari implementasi ? Katakan dengan mengambil informasi yang cukup dari segmen kode yang berbeda ini untuk mengidentifikasi mereka ke seluruh program, dan menempatkan mereka dalam semacam file header ? Dan dengan begitu, saya dapat menggunakan #include arahan preprosesor untuk hanya membawa informasi yang diperlukan untuk dikompilasi! "

Hmm. Anda mungkin ke sesuatu di sana. Biarkan saya tahu cara kerjanya bagi Anda.

goldpseudo
sumber
13
Jawaban yang bagus, tuan. Itu menyenangkan dibaca, dan mudah dimengerti. Saya berharap buku teks saya ditulis seperti ini.
ialm
@veol Mencari Kepala Seri pertama buku - Saya tidak tahu apakah mereka memiliki versi C ++. headfirstlabs.com
Amarghosh
2
Ini (pasti) kata-kata terbaik sampai saat ini yang pernah saya dengar atau renungkan. Justin Case, seorang pemula yang cakap, mencapai satu proyek dengan satu juta penekanan tombol, belum dikirim dan "proyek pertama" yang patut dipuji yang melihat cahaya aplikasi dalam basis pengguna nyata, telah mengenali masalah yang ditangani oleh penutupan. Kedengarannya sangat mirip dengan negara maju definisi masalah asli OP minus "kode ini hampir seratus kali dan tidak dapat menemukan apa yang harus dilakukan untuk null (karena tidak ada objek) vs null (sebagai keponakan) tanpa menggunakan pemrograman dengan pengecualian."
Nicholas Jordan
Tentu saja ini semua berantakan untuk templat karena kebanyakan kompiler tidak mendukung / mengimplementasikan kata kunci 'ekspor'.
KitsuneYMG
1
Poin lain adalah bahwa Anda memiliki banyak perpustakaan yang canggih (jika berpikir tentang BOOST) yang menggunakan kelas header saja ... Ho, tunggu? Mengapa programmer berpengalaman tidak memisahkan antarmuka dari implementasi? Sebagian dari jawabannya mungkin apa yang dikatakan Blindly, bagian lain mungkin satu file lebih baik dari dua jika memungkinkan, dan bagian lain adalah bahwa menghubungkan memiliki biaya daripada yang bisa cukup tinggi. Saya telah melihat program berjalan sepuluh kali lebih cepat dengan pemasukan sumber dan pengoptimal secara langsung. Karena menghubungkan sebagian besar blok optimasi.
Kriss
45

Ini mungkin jawaban yang lebih rinci daripada yang Anda inginkan, tetapi saya pikir penjelasan yang layak dibenarkan.

Dalam C dan C ++, satu file sumber didefinisikan sebagai satu unit terjemahan . Secara konvensi, file header memiliki deklarasi fungsi, ketik definisi, dan definisi kelas. Implementasi fungsi aktual berada di unit terjemahan, yaitu file .cpp.

Gagasan di balik ini adalah bahwa fungsi dan fungsi anggota kelas / struct dikompilasi dan dirakit sekali, maka fungsi lain dapat memanggil kode itu dari satu tempat tanpa membuat duplikat. Fungsi Anda dinyatakan sebagai "eksternal" secara implisit.

/* Function declaration, usually found in headers. */
/* Implicitly 'extern', i.e the symbol is visible everywhere, not just locally.*/
int add(int, int);

/* function body, or function definition. */
int add(int a, int b) 
{
   return a + b;
}

Jika Anda ingin fungsi menjadi lokal untuk unit terjemahan, Anda mendefinisikannya sebagai 'statis'. Apa artinya ini? Ini berarti bahwa jika Anda memasukkan file sumber dengan fungsi eksternal, Anda akan mendapatkan kesalahan redefinisi, karena kompiler menemukan implementasi yang sama lebih dari sekali. Jadi, Anda ingin semua unit terjemahan Anda melihat deklarasi fungsi tetapi bukan badan fungsi .

Jadi bagaimana semua itu dihaluskan bersama pada akhirnya? Itu adalah tugas penghubung. Linker membaca semua file objek yang dihasilkan oleh tahap assembler dan menyelesaikan simbol. Seperti yang saya katakan sebelumnya, simbol hanyalah sebuah nama. Misalnya, nama variabel atau fungsi. Ketika unit terjemahan yang memanggil fungsi atau menyatakan tipe tidak mengetahui implementasi untuk fungsi atau tipe tersebut, simbol tersebut dikatakan tidak terselesaikan. Linker menyelesaikan simbol yang tidak terselesaikan dengan menghubungkan unit terjemahan yang menyimpan simbol yang tidak ditentukan bersama dengan yang berisi implementasi. Fiuh. Ini berlaku untuk semua simbol yang terlihat secara eksternal, apakah simbol tersebut diimplementasikan dalam kode Anda, atau disediakan oleh perpustakaan tambahan. Perpustakaan sebenarnya hanya arsip dengan kode yang dapat digunakan kembali.

Ada dua pengecualian penting. Pertama, jika Anda memiliki fungsi kecil, Anda bisa membuatnya sejajar. Ini berarti bahwa kode mesin yang dihasilkan tidak menghasilkan panggilan fungsi eksternal, tetapi secara harfiah digabungkan di tempat. Karena mereka biasanya kecil, ukuran overhead tidak masalah. Anda dapat membayangkan mereka statis dalam cara mereka bekerja. Jadi aman untuk mengimplementasikan fungsi inline di header. Implementasi fungsi di dalam kelas atau definisi struct juga sering diuraikan secara otomatis oleh kompiler.

Pengecualian lainnya adalah templat. Karena kompiler perlu melihat definisi tipe templat keseluruhan saat membuat instance, tidak mungkin memisahkan implementasi dari definisi tersebut dengan fungsi mandiri atau kelas normal. Yah, mungkin ini mungkin sekarang, tetapi mendapatkan dukungan kompiler yang tersebar luas untuk kata kunci "ekspor" membutuhkan waktu yang sangat lama. Jadi tanpa dukungan untuk 'ekspor', unit terjemahan mendapatkan salinan lokal mereka sendiri dari jenis dan fungsi templated instantiated, mirip dengan bagaimana fungsi inline bekerja. Dengan dukungan untuk 'ekspor', ini tidak terjadi.

Untuk dua pengecualian, beberapa orang merasa "lebih baik" untuk meletakkan implementasi fungsi inline, fungsi templated, dan tipe templated dalam file .cpp, dan kemudian # sertakan file .cpp. Apakah ini header atau file sumber tidak terlalu penting; preprocessor tidak peduli dan hanya sebuah konvensi.

Ringkasan cepat seluruh proses mulai dari kode C ++ (beberapa file) dan hingga yang dapat dieksekusi akhir:

  • The preprocessor dijalankan, yang mem-parsing semua arahan yang dimulai dengan '#'. Arahan #include menggabungkan file yang disertakan dengan inferior, misalnya. Ini juga melakukan penggantian makro dan menempelkan token.
  • Compiler aktual berjalan pada file teks perantara setelah tahap preprocessor, dan memancarkan kode assembler.
  • The assembler berjalan pada file perakitan dan memancarkan kode mesin, ini biasanya disebut file objek dan mengikuti format biner dari sistem operasi tersebut. Sebagai contoh, Windows menggunakan PE (format portable executable), sedangkan Linux menggunakan format Unix System V ELF, dengan ekstensi GNU. Pada tahap ini, simbol masih ditandai sebagai tidak terdefinisi.
  • Akhirnya, linker dijalankan. Semua tahap sebelumnya dijalankan pada setiap unit terjemahan secara berurutan. Namun, tahap tautan bekerja pada semua file objek yang dihasilkan yang dihasilkan oleh assembler. Linker menyelesaikan simbol dan melakukan banyak keajaiban seperti membuat bagian dan segmen, yang tergantung pada platform target dan format biner. Pemrogram tidak diharuskan untuk mengetahui hal ini secara umum, tetapi pasti membantu dalam beberapa kasus.

Sekali lagi, ini jelas lebih dari yang Anda minta, tapi saya harap detail seluk beluk membantu Anda melihat gambaran yang lebih besar.

melpomene
sumber
2
Terima kasih atas penjelasan lengkapnya. Saya akui, itu belum masuk akal bagi saya, dan saya pikir saya perlu membaca jawaban Anda lagi (dan lagi).
ialm
1
+1 untuk penjelasan yang bagus. Sayang sekali itu mungkin akan menakuti semua pemula C ++. :)
goldPseudo
1
Heh, jangan merasa buruk. Pada Stack Overflow, jawaban terpanjang jarang merupakan jawaban terbaik.
int add(int, int);adalah deklarasi fungsi . Bagian prototipe itu adil int, int. Namun, semua fungsi dalam C ++ memiliki prototipe, jadi istilah ini benar-benar masuk akal dalam C. Saya telah mengedit jawaban Anda untuk efek ini.
melpomene
exportuntuk templat telah dihapus dari bahasa pada tahun 2011. Itu tidak pernah benar-benar didukung oleh kompiler.
melpomene
10

Solusi khasnya adalah menggunakan .hfile hanya untuk deklarasi dan .cppfile untuk implementasi. Jika Anda perlu menggunakan kembali implementasi Anda memasukkan .hfile yang sesuai ke dalam .cppfile di mana kelas / fungsi / apa pun yang diperlukan digunakan dan tautan ke .cppfile yang sudah dikompilasi (baik .objfile - biasanya digunakan dalam satu proyek - atau file .lib - biasanya digunakan untuk digunakan kembali dari berbagai proyek). Dengan cara ini Anda tidak perlu mengkompilasi ulang semuanya jika hanya implementasi yang berubah.

sharptooth
sumber
6

Pikirkan file cpp sebagai kotak hitam dan file .h sebagai panduan tentang cara menggunakan kotak hitam itu.

File cpp dapat dikompilasi sebelumnya. Ini tidak berfungsi di dalam Anda # sertakan mereka, karena perlu "sertakan" kode yang sebenarnya ke dalam program Anda setiap kali dikompilasi. Jika Anda hanya menyertakan header, itu bisa menggunakan file header untuk menentukan bagaimana menggunakan file cpp yang telah dikompilasi.

Meskipun ini tidak akan membuat banyak perbedaan untuk proyek pertama Anda, jika Anda mulai menulis program cpp besar, orang-orang akan membenci Anda karena waktu kompilasi akan meledak.

Juga baca ini: File Header Sertakan Pola

Dan McGrath
sumber
Terima kasih atas contoh yang lebih nyata. Saya mencoba membaca tautan Anda, tetapi sekarang saya bingung ... apa perbedaan antara memasukkan tajuk secara eksplisit dan pernyataan penerusan?
ialm
ini artikel yang bagus. Veol, di sini mereka termasuk header di mana kompiler membutuhkan informasi mengenai ukuran kelas. Deklarasi maju digunakan ketika Anda hanya menggunakan pointer.
pankajt
pernyataan maju: int someFunction (int neededValue); perhatikan penggunaan informasi jenis dan (biasanya) tidak ada kurung kurawal. Ini, seperti yang diberikan, memberitahu kompiler bahwa pada titik tertentu Anda akan memerlukan fungsi yang mengambil int dan mengembalikan int, kompiler dapat memesan panggilan untuk itu menggunakan informasi ini. Itu akan disebut deklarasi maju. Compiler yang lebih canggih seharusnya dapat menemukan fungsi tanpa perlu ini, termasuk header dapat menjadi cara yang mudah untuk mendeklarasikan sekelompok deklarasi maju.
Nicholas Jordan
6

File header biasanya berisi deklarasi fungsi / kelas, sedangkan file .cpp berisi implementasi yang sebenarnya. Pada waktu kompilasi, setiap file .cpp akan dikompilasi menjadi file objek (biasanya ekstensi .o), dan linker menggabungkan berbagai file objek ke dalam executable akhir. Proses penautan umumnya jauh lebih cepat daripada kompilasi.

Manfaat dari pemisahan ini: Jika Anda mengkompilasi ulang salah satu file .cpp di proyek Anda, Anda tidak perlu mengkompilasi ulang semua yang lain. Anda cukup membuat file objek baru untuk file .cpp tertentu. Kompiler tidak harus melihat file .cpp lainnya. Namun, jika Anda ingin memanggil fungsi dalam file .cpp Anda saat ini yang diimplementasikan dalam file .cpp lainnya, Anda harus memberi tahu kompilator argumen apa yang mereka ambil; itulah tujuan memasukkan file header.

Kekurangan: Ketika mengkompilasi file .cpp yang diberikan, kompiler tidak dapat 'melihat' apa yang ada di dalam file .cpp lainnya. Jadi tidak tahu bagaimana fungsi-fungsi yang ada diimplementasikan, dan sebagai hasilnya tidak dapat mengoptimalkan secara agresif. Tapi saya pikir Anda belum perlu memikirkannya sendiri (:

int3
sumber
5

Gagasan dasar bahwa header hanya disertakan dan file cpp hanya dikompilasi. Ini akan menjadi lebih bermanfaat setelah Anda memiliki banyak file cpp, dan mengkompilasi ulang seluruh aplikasi saat Anda memodifikasi hanya satu di antaranya yang akan terlalu lambat. Atau kapan fungsi dalam file akan mulai tergantung satu sama lain. Jadi, Anda harus memisahkan deklarasi kelas ke dalam file header Anda, meninggalkan implementasi dalam file cpp dan menulis Makefile (atau sesuatu yang lain, tergantung pada alat apa yang Anda gunakan) untuk mengkompilasi file cpp dan menautkan file objek yang dihasilkan ke dalam suatu program.

Lukáš Lalinský
sumber
3

Jika Anda #memasukkan file cpp ke beberapa file lain di program Anda, kompiler akan mencoba mengkompilasi file cpp beberapa kali, dan akan menghasilkan kesalahan karena akan ada beberapa implementasi dari metode yang sama.

Kompilasi akan memakan waktu lebih lama (yang menjadi masalah pada proyek-proyek besar), jika Anda mengedit dalam file cpp #included, yang kemudian memaksa kompilasi ulang file apa pun #termasuk mereka.

Cukup masukkan deklarasi Anda ke file header dan sertakan (karena mereka tidak benar-benar menghasilkan kode per se), dan linker akan menghubungkan deklarasi dengan kode cpp yang sesuai (yang kemudian hanya dikompilasi sekali).

NeilDurant
sumber
Jadi, selain memiliki waktu kompilasi yang lebih lama, saya akan mulai mengalami masalah ketika saya # mencantumkan file cpp saya di banyak file berbeda yang menggunakan fungsi dalam file cpp yang disertakan?
ialm
Ya, ini disebut tabrakan namespace. Yang menarik di sini adalah apakah menghubungkan dengan lib memperkenalkan masalah namespace. Secara umum, saya menemukan bahwa kompiler menghasilkan waktu kompilasi yang lebih baik untuk lingkup unit terjemahan (semua dalam satu file) yang memperkenalkan masalah namespace - yang mengarah ke pemisahan lagi .... Anda dapat memasukkan file include di setiap unit terjemahan, (seharusnya) bahkan ada pragma (#pragma sekali) yang seharusnya menegakkan ini tetapi itu adalah anggapan supositoria. Berhati-hatilah untuk tidak bergantung secara buta pada lib (file .O) dari mana pun karena tautan 32-bit tidak ditegakkan.
Nicholas Jordan
2

Meskipun tentu saja mungkin untuk melakukan seperti yang Anda lakukan, praktik standarnya adalah menempatkan deklarasi bersama ke dalam file header (.h), dan definisi fungsi dan variabel - implementasi - ke dalam file sumber (.cpp).

Sebagai konvensi, ini membantu memperjelas di mana semuanya berada, dan membuat perbedaan yang jelas antara antarmuka dan implementasi modul Anda. Ini juga berarti bahwa Anda tidak perlu memeriksa untuk melihat apakah file .cpp termasuk dalam yang lain, sebelum menambahkan sesuatu ke dalamnya yang dapat pecah jika didefinisikan dalam beberapa unit yang berbeda.

Avi
sumber
2

re-usability, arsitektur dan enkapsulasi data

ini sebuah contoh:

katakanlah Anda membuat file cpp yang berisi bentuk sederhana dari rutinitas string semua dalam kelas mystring, Anda menempatkan kelas mendeklarasikan ini dalam mystring.h kompilasi mystring.cpp ke file .obj

sekarang di program utama Anda (mis. main.cpp) Anda memasukkan header dan tautan dengan mystring.obj. untuk menggunakan mystring di program Anda, Anda tidak peduli tentang detail bagaimana mystring diimplementasikan karena header mengatakan apa yang bisa dilakukan

sekarang jika seorang teman ingin menggunakan kelas mistis Anda, Anda memberinya mystring.h dan mystring.obj, dia juga tidak perlu tahu cara kerjanya selama itu bekerja.

nanti jika Anda memiliki lebih banyak file .obj tersebut, Anda dapat menggabungkannya menjadi file .lib dan menautkannya.

Anda juga dapat memutuskan untuk mengubah file mystring.cpp dan mengimplementasikannya secara lebih efektif, ini tidak akan memengaruhi main.cpp Anda atau program teman Anda.

AndersK
sumber
2

Jika itu bekerja untuk Anda, maka tidak ada yang salah dengan itu - kecuali bahwa itu akan mengacak-acak bulu orang yang berpikir bahwa hanya ada satu cara untuk melakukan sesuatu.

Banyak jawaban yang diberikan di sini membahas optimasi untuk proyek perangkat lunak skala besar. Ini adalah hal-hal yang baik untuk diketahui, tetapi tidak ada gunanya mengoptimalkan proyek kecil seolah-olah itu adalah proyek besar - itulah yang dikenal sebagai "optimasi prematur". Tergantung pada lingkungan pengembangan Anda, mungkin ada kompleksitas tambahan yang signifikan yang terlibat dalam pengaturan konfigurasi pembangunan untuk mendukung beberapa file sumber per program.

Jika, seiring berjalannya waktu, proyek Anda berkembang dan Anda merasa bahwa proses pembuatannya terlalu lama, maka Anda dapat memperbaiki kode Anda untuk menggunakan banyak file sumber untuk pembuatan bertahap yang lebih cepat.

Beberapa jawaban membahas pemisahan antarmuka dari implementasi. Namun, ini bukan fitur bawaan dari file sertakan, dan sangat umum untuk memasukkan # "header" file yang secara langsung memasukkan implementasinya (bahkan C ++ Standard Library melakukan ini pada tingkat yang signifikan).

Satu-satunya hal yang benar-benar "tidak konvensional" tentang apa yang telah Anda lakukan adalah memberi nama file yang disertakan ".cpp" alih-alih ".h" atau ".hpp".

Brent Bradburn
sumber
1

Ketika Anda mengkompilasi dan menautkan suatu program, kompiler pertama-tama mengkompilasi masing-masing file cpp dan kemudian mereka menautkan (menghubungkan) mereka. Header tidak akan pernah dikompilasi, kecuali dimasukkan dalam file cpp terlebih dahulu.

Biasanya header adalah deklarasi dan cpp adalah file implementasi. Di header Anda mendefinisikan antarmuka untuk kelas atau fungsi tetapi Anda mengabaikan bagaimana Anda benar-benar mengimplementasikan detail. Dengan cara ini Anda tidak perlu mengkompilasi ulang setiap file cpp jika Anda mengubahnya.

Jonas
sumber
jika Anda meninggalkan implementasi dari file header permisi tetapi itu terdengar seperti antarmuka Java untuk saya kan?
gansub
1

Saya akan menyarankan Anda untuk pergi melalui Desain Software C ++ Skala Besar oleh John Lakos . Di kampus, kami biasanya menulis proyek kecil di mana kami tidak menemukan masalah seperti itu. Buku ini menyoroti pentingnya memisahkan antarmuka dan implementasinya.

File header biasanya memiliki antarmuka yang seharusnya tidak terlalu sering diubah. Demikian pula dengan melihat pola seperti idiom Virtual Constructor akan membantu Anda memahami konsep lebih lanjut.

Saya masih belajar seperti Anda :)

pankajt
sumber
Terima kasih untuk saran buku. Aku tidak tahu apakah aku akan pernah sampai ke tahap pembuatan skala besar C ++ program meskipun ...
ialm
itu menyenangkan untuk kode program skala besar dan banyak tantangan. Saya mulai menyukainya :)
pankajt
1

Ini seperti menulis buku, Anda hanya ingin mencetak bab yang sudah selesai

Katakanlah Anda sedang menulis buku. Jika Anda meletakkan bab-bab dalam file yang terpisah maka Anda hanya perlu mencetak bab jika Anda telah mengubahnya. Bekerja pada satu bab tidak mengubah yang lain.

Tetapi termasuk file cpp, dari sudut pandang kompiler, seperti mengedit semua bab buku dalam satu file. Kemudian jika Anda mengubahnya, Anda harus mencetak semua halaman dari seluruh buku untuk mendapatkan bab revisi Anda dicetak. Tidak ada opsi "cetak halaman yang dipilih" dalam pembuatan kode objek.

Kembali ke perangkat lunak: Saya memiliki Linux dan Ruby src tergeletak di sana. Ukuran garis kode yang kasar ...

     Linux       Ruby
   100,000    100,000   core functionality (just kernel/*, ruby top level dir)
10,000,000    200,000   everything 

Salah satu dari empat kategori tersebut memiliki banyak kode, karenanya diperlukan modularitas. Basis kode semacam ini sangat tipikal untuk sistem dunia nyata.

DigitalRoss
sumber