Di: http://www.learncpp.com/cpp-tutorial/19-header-files/
Berikut ini disebutkan:
add.cpp:
int add(int x, int y)
{
return x + y;
}
main.cpp:
#include <iostream>
int add(int x, int y); // forward declaration using function prototype
int main()
{
using namespace std;
cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
return 0;
}
Kami menggunakan deklarasi maju sehingga kompiler akan tahu apa "
add
" saat mengkompilasimain.cpp
. Seperti yang disebutkan sebelumnya, menulis deklarasi maju untuk setiap fungsi yang ingin Anda gunakan yang hidup di file lain bisa membosankan dengan cepat.
Bisakah Anda menjelaskan lebih lanjut " deklarasi maju "? Apa masalahnya jika kita menggunakannya dalam main()
fungsinya?
c++
declaration
forward-declaration
Kesederhanaan
sumber
sumber
Jawaban:
Mengapa forward-mendeklarasikan diperlukan dalam C ++
Kompiler ingin memastikan Anda tidak membuat kesalahan pengejaan atau memberikan jumlah argumen yang salah ke fungsi. Jadi, ini menegaskan bahwa ia pertama kali melihat deklarasi 'add' (atau jenis, kelas, atau fungsi lainnya) sebelum digunakan.
Ini benar-benar hanya memungkinkan kompiler untuk melakukan pekerjaan yang lebih baik dalam memvalidasi kode, dan memungkinkannya untuk merapikan ujung yang longgar sehingga dapat menghasilkan file objek yang tampak rapi. Jika Anda tidak perlu meneruskan mendeklarasikan sesuatu, kompiler akan menghasilkan file objek yang harus berisi informasi tentang semua dugaan yang mungkin tentang apa fungsi 'add' mungkin. Dan linker harus mengandung logika yang sangat pintar untuk mencoba dan mencari tahu 'add' yang ingin Anda panggil, ketika fungsi 'add' dapat hidup dalam file objek yang berbeda, linker bergabung dengan yang menggunakan add untuk menghasilkan dll atau exe. Mungkin saja linker mendapatkan tambahan yang salah. Katakanlah Anda ingin menggunakan int add (int a, float b), tetapi tidak sengaja lupa untuk menuliskannya, tetapi linker menemukan int add yang sudah ada (int a, int b) dan berpikir itu yang benar dan menggunakannya. Kode Anda akan dikompilasi, tetapi tidak akan melakukan apa yang Anda harapkan.
Jadi, hanya untuk membuat hal-hal eksplisit dan menghindari tebakan dll, kompiler bersikeras Anda menyatakan semuanya sebelum digunakan.
Perbedaan antara deklarasi dan definisi
Selain itu, penting untuk mengetahui perbedaan antara deklarasi dan definisi. Deklarasi hanya memberikan kode yang cukup untuk menunjukkan seperti apa sesuatu itu, jadi untuk suatu fungsi, ini adalah tipe pengembalian, konvensi pemanggilan, nama metode, argumen dan tipenya. Tetapi kode untuk metode ini tidak diperlukan. Untuk sebuah definisi, Anda memerlukan deklarasi dan juga kode untuk fungsinya juga.
Bagaimana deklarasi maju dapat secara signifikan mengurangi waktu pembuatan
Anda bisa memasukkan deklarasi fungsi ke file .cpp atau .h Anda saat ini dengan # menyertakan header yang sudah berisi deklarasi fungsi. Namun, ini dapat memperlambat kompilasi Anda, terutama jika Anda # memasukkan header ke dalam .h alih-alih .cpp dari program Anda, karena segala sesuatu yang # termasuk .h yang sedang Anda tulis akan berakhir # termasuk semua header Anda menulis #termasuk juga. Tiba-tiba, kompiler memiliki #include halaman dan halaman kode yang perlu dikompilasi bahkan ketika Anda hanya ingin menggunakan satu atau dua fungsi. Untuk menghindari ini, Anda dapat menggunakan deklarasi maju dan cukup ketik sendiri deklarasi fungsi di bagian atas file. Jika Anda hanya menggunakan beberapa fungsi, ini benar-benar dapat membuat kompilasi Anda lebih cepat dibandingkan dengan selalu menyertakan header. Untuk proyek yang sangat besar,
Pecahkan referensi siklik di mana dua definisi saling menggunakan
Selain itu, deklarasi maju dapat membantu Anda memutus siklus. Di sinilah dua fungsi keduanya mencoba menggunakan satu sama lain. Ketika ini terjadi (dan ini adalah hal yang benar-benar sah untuk dilakukan), Anda dapat #mengikutsertakan satu file header, tetapi file header itu mencoba untuk #mengikutsertakan file header yang sedang Anda tulis .... yang kemudian #termasuk header lainnya , yang termasuk # yang Anda tulis. Anda terjebak dalam situasi ayam dan telur dengan masing-masing file header mencoba memasukkan # lainnya. Untuk mengatasi ini, Anda dapat meneruskan-mendeklarasikan bagian-bagian yang Anda butuhkan di salah satu file dan meninggalkan #sertakan dari file itu.
Misalnya:
File Car.h
File Wheel.h
Hmm ... deklarasi Mobil diperlukan di sini karena Wheel memiliki pointer ke Mobil, tetapi Car.h tidak dapat dimasukkan di sini karena akan menghasilkan kesalahan kompiler. Jika Car.h dimasukkan, maka akan mencoba untuk memasukkan Wheel.h yang akan mencakup Car.h yang akan mencakup Wheel.h dan ini akan berlangsung selamanya, jadi sebagai gantinya kompiler menimbulkan kesalahan. Solusinya adalah dengan meneruskan mendeklarasikan Mobil:
Jika Roda kelas memiliki metode yang perlu memanggil metode mobil, metode tersebut dapat didefinisikan dalam Wheel.cpp dan Wheel.cpp sekarang dapat memasukkan Car.h tanpa menyebabkan siklus.
sumber
// From Car.h
Anda dapat membuat beberapa situasi berbulu mencoba menemukan definisi di ujung jalan, dijamin.Kompilator mencari setiap simbol yang digunakan dalam unit terjemahan saat ini dinyatakan sebelumnya atau tidak dalam unit saat ini. Ini hanya masalah gaya menyediakan semua tanda tangan metode di awal file sumber sementara definisi diberikan nanti. Penggunaan signifikan dari itu adalah ketika Anda menggunakan pointer ke kelas sebagai variabel anggota kelas lain.
Jadi, gunakan deklarasi maju di kelas kapan pun memungkinkan. Jika program Anda hanya memiliki fungsi (dengan file tajuk ho), maka memberikan prototipe di awal hanya masalah gaya. Bagaimanapun, ini terjadi jika file header ada dalam program normal dengan header yang hanya memiliki fungsi.
sumber
Karena C ++ diuraikan dari atas ke bawah, kompiler perlu tahu tentang hal-hal sebelum mereka digunakan. Jadi, ketika Anda referensi:
pada fungsi utama, kompiler perlu mengetahuinya. Untuk membuktikan ini coba pindahkan ke bawah fungsi utama dan Anda akan mendapatkan kesalahan kompiler.
Jadi ' Deklarasi Teruskan ' adalah apa yang tertulis di kaleng. Ini menyatakan sesuatu sebelum penggunaannya.
Secara umum Anda akan menyertakan penerusan deklarasi dalam file header dan kemudian memasukkan file header dengan cara yang sama seperti iostream disertakan.
sumber
Istilah " pernyataan maju " di C ++ sebagian besar hanya digunakan untuk deklarasi kelas . Lihat (akhir) jawaban ini untuk alasan mengapa "deklarasi maju" suatu kelas benar-benar hanyalah deklarasi kelas sederhana dengan nama mewah.
Dengan kata lain, "maju" hanya menambah pemberat istilah, karena setiap deklarasi dapat dilihat sebagai maju sejauh itu menyatakan beberapa pengidentifikasi sebelum digunakan.
(Mengenai apakah deklarasi yang bertentangan dengan definisi , lihat lagi Apa perbedaan antara definisi dan deklarasi? )
sumber
Ketika kompilator melihatnya
add(3, 4)
perlu tahu apa artinya itu. Dengan deklarasi forward Anda pada dasarnya memberitahu kompiler ituadd
adalah fungsi yang mengambil dua int dan mengembalikan sebuah int. Ini adalah informasi penting untuk kompiler karena ia perlu menempatkan 4 dan 5 di representasi yang benar ke stack dan perlu tahu apa jenis barang yang dikembalikan oleh add.Pada saat itu, compiler tidak khawatir tentang yang sebenarnya pelaksanaan
add
, yaitu di mana itu (atau jika ada yang bahkan satu) dan jika mengkompilasi. Itu muncul kemudian, setelah mengkompilasi file sumber ketika linker dipanggil.sumber
Itu sama dengan
#include"add.h"
. Jika Anda tahu, preprocessor memperluas file yang Anda sebutkan#include
, dalam file .cpp di mana Anda menulis#include
arahan. Itu artinya, jika Anda menulis#include"add.h"
, Anda mendapatkan hal yang sama, seolah-olah Anda melakukan "pernyataan maju".Saya berasumsi bahwa
add.h
memiliki baris ini:sumber
satu tambahan cepat tentang: biasanya Anda menempatkan referensi ke dalam file header milik file .c (pp) di mana fungsi / variabel dll diimplementasikan. dalam contoh Anda akan terlihat seperti ini: add.h:
kata kunci extern menyatakan bahwa fungsi tersebut sebenarnya dideklarasikan dalam file eksternal (bisa juga berupa perpustakaan, dll.). main.c Anda akan terlihat seperti ini:
sumber
Satu masalah adalah, bahwa kompiler tidak tahu, jenis nilai apa yang dikirimkan oleh fungsi Anda; Diasumsikan, bahwa fungsi mengembalikan sebuah
int
dalam kasus ini, tetapi ini bisa benar karena dapat salah. Masalah lain adalah, bahwa kompiler tidak tahu, argumen apa yang diharapkan fungsi Anda, dan tidak bisa memperingatkan Anda, jika Anda memberikan nilai yang salah. Ada aturan "promosi" khusus, yang berlaku ketika lewat, katakan nilai floating point ke fungsi yang tidak dideklarasikan (kompiler harus melebarkannya untuk mengetik ganda), yang seringkali tidak, apa fungsi sebenarnya harapkan, yang menyebabkan sulit untuk menemukan bug saat run-time.sumber