Fungsi sebaris (tertanam). Kelebihan fungsi

Kelebihan fungsi

Overloading operasi (operator, fungsi, prosedur)- dalam pemrograman - salah satu cara untuk mengimplementasikan polimorfisme, yang terdiri dari kemungkinan keberadaan simultan dalam satu ruang lingkup beberapa berbagai pilihan operasi (pernyataan, fungsi, atau prosedur) yang memiliki nama yang sama tetapi berbeda dalam jenis parameter yang diterapkan.

Terminologi

Istilah "overloading" adalah kertas kalkir dari "overloading" bahasa Inggris, yang muncul dalam terjemahan bahasa Rusia dari buku-buku tentang bahasa pemrograman pada paruh pertama tahun 1990-an. Mungkin ini bukan yang paling pilihan terbaik terjemahan, karena kata "kelebihan beban" dalam bahasa Rusia memiliki arti yang mapan, sangat berbeda dari yang baru diusulkan, namun, kata itu telah mengakar dan tersebar luas. Dalam publikasi era Soviet, mekanisme serupa disebut dalam bahasa Rusia "redefinisi" atau "redefinisi" operasi, tetapi opsi ini tidak dapat diperdebatkan: ketidaksesuaian dan kebingungan muncul dalam terjemahan bahasa Inggris "override", "overload" dan " definisikan ulang”.

Alasan penampilan

Sebagian besar bahasa pemrograman awal memiliki batasan bahwa tidak lebih dari satu operasi dengan nama yang sama dapat tersedia dalam suatu program pada waktu yang sama. Oleh karena itu, semua fungsi dan prosedur yang terlihat pada titik tertentu dalam program harus memiliki nama yang berbeda. Nama dan sebutan fungsi, prosedur, dan operator yang merupakan bagian dari bahasa pemrograman tidak dapat digunakan oleh programmer untuk menamai fungsi, prosedur, dan operatornya sendiri. Dalam beberapa kasus, seorang programmer dapat membuat objek programnya sendiri dengan nama objek lain yang sudah ada, tetapi kemudian objek yang baru dibuat "tumpang tindih" dengan yang sebelumnya, dan menjadi tidak mungkin untuk menggunakan kedua opsi secara bersamaan.

Situasi ini tidak nyaman dalam beberapa kasus yang cukup umum.

  • Terkadang ada kebutuhan untuk mendeskripsikan dan menerapkan operasi ke tipe data yang dibuat oleh programmer yang setara artinya dengan yang sudah tersedia dalam bahasa tersebut. Contoh klasik adalah perpustakaan untuk bekerja dengan bilangan kompleks. Mereka, seperti tipe numerik biasa, mendukung operasi aritmatika, dan wajar untuk dibuat dari jenis ini operasi "plus", "minus", "kalikan", "bagi", yang menunjukkannya dengan tanda operasi yang sama seperti untuk jenis numerik lainnya. Larangan penggunaan elemen yang didefinisikan dalam bahasa memaksa pembuatan banyak fungsi dengan nama seperti ComplexPlusComplex, IntegerPlusComplex, ComplexMinusFloat, dan sebagainya.
  • Ketika operasi dengan arti yang sama diterapkan ke operan berbagai jenis, mereka harus diberi nama berbeda. Ketidakmampuan untuk melamar jenis yang berbeda fungsi dengan nama yang sama mengarah pada kebutuhan untuk menemukan nama yang berbeda untuk hal yang sama, yang menimbulkan kebingungan, dan dapat menyebabkan kesalahan. Misalnya, dalam bahasa C klasik, ada dua versi fungsi perpustakaan standar untuk menemukan modulus angka: abs() dan fabs() - yang pertama ditujukan untuk argumen bilangan bulat, yang kedua untuk argumen nyata. Situasi ini, dikombinasikan dengan pemeriksaan tipe C yang lemah, dapat menyebabkan kesalahan yang sulit ditemukan: jika programmer menulis abs(x) dalam perhitungan, di mana x adalah variabel nyata, maka beberapa kompiler akan menghasilkan kode tanpa peringatan yang akan ubah x menjadi bilangan bulat dengan membuang bagian pecahan dan hitung modulus dari bilangan bulat yang dihasilkan!

Sebagian, masalah diselesaikan dengan pemrograman objek - ketika tipe data baru dideklarasikan sebagai kelas, operasi pada mereka dapat diformalkan sebagai metode kelas, termasuk metode kelas dengan nama yang sama (karena metode dari kelas yang berbeda tidak harus memiliki nama yang berbeda), tetapi, pertama, cara desain operasi pada nilai dari berbagai jenis tidak nyaman, dan kedua, tidak menyelesaikan masalah membuat operator baru.

Kelebihan operator itu sendiri hanyalah "gula sintaksis", meskipun demikian itu dapat berguna karena memungkinkan pengembang untuk memprogram dengan cara yang lebih alami dan membuat tipe khusus berperilaku lebih seperti yang ada di dalamnya. Jika kami mendekati masalah dari posisi yang lebih umum, maka kami dapat melihat bahwa alat yang memungkinkan Anda memperluas bahasa, melengkapinya dengan operasi baru dan konstruksi sintaksis (dan kelebihan operasi adalah salah satu alat tersebut, bersama dengan objek, makro , fungsional, penutup) mengubahnya menjadi metabahasa - sarana untuk mendeskripsikan bahasa yang berorientasi pada tugas tertentu. Dengan bantuannya, dimungkinkan untuk membangun ekstensi bahasa untuk setiap tugas tertentu yang paling sesuai untuknya, yang memungkinkan untuk mendeskripsikan solusinya dalam bentuk yang paling alami, mudah dipahami, dan sederhana. Misalnya, dalam aplikasi untuk membebani operasi: membuat perpustakaan tipe matematika kompleks (vektor, matriks) dan mendeskripsikan operasi dengannya dalam bentuk "matematis" alami, menciptakan "bahasa untuk operasi vektor", di mana kompleksitas perhitungan disembunyikan, dan dimungkinkan untuk menggambarkan solusi masalah dalam bentuk operasi vektor dan matriks, dengan fokus pada esensi masalah, bukan pada tekniknya. Karena alasan inilah cara seperti itu pernah dimasukkan dalam bahasa Algol-68.

Mekanisme beban berlebih

Penerapan

Kelebihan operator melibatkan pengenalan dua fitur yang saling terkait ke dalam bahasa: kemampuan untuk mendeklarasikan beberapa prosedur atau fungsi dengan nama yang sama dalam lingkup yang sama, dan kemampuan untuk mendeskripsikan implementasi operasi Anda sendiri (yaitu, tanda-tanda operasi, biasanya ditulis dalam notasi infiks, di antara operand). Pada dasarnya, penerapannya cukup sederhana:

  • Untuk memungkinkan adanya beberapa operasi dengan nama yang sama, cukup memperkenalkan aturan ke dalam bahasa, yang dengannya suatu operasi (prosedur, fungsi, atau operator) dikenali oleh kompiler tidak hanya dengan nama (notasi), tetapi juga berdasarkan jenis parameternya. Jadi abs(i), dimana i dinyatakan sebagai integer, dan abs(x), dimana x dinyatakan sebagai real, adalah dua operasi yang berbeda. Pada dasarnya, tidak ada kesulitan dalam memberikan interpretasi seperti itu.
  • Untuk mengaktifkan operasi pendefinisian dan pendefinisian ulang, konstruksi sintaksis yang sesuai perlu diperkenalkan ke dalam bahasa. Pilihannya bisa cukup banyak, tapi nyatanya tidak berbeda satu sama lain, cukup diingat bahwa entri formulir “<операнд1> <знакОперации> <операнд2>» pada dasarnya mirip dengan memanggil fungsi «<знакОперации>(<операнд1>,<операнд2>)". Ini cukup untuk memungkinkan pemrogram mendeskripsikan perilaku operator dalam bentuk fungsi - dan masalah deskripsi terpecahkan.

Pilihan dan masalah

Overloading prosedur dan fungsi di tingkat ide umum, sebagai aturan, tidak sulit untuk diterapkan atau dipahami. Namun, di dalamnya pun ada beberapa "jebakan" yang harus diperhatikan. Mengizinkan kelebihan operator menciptakan lebih banyak masalah bagi pelaksana bahasa dan pemrogram yang bekerja dalam bahasa itu.

Masalah identifikasi

Pertanyaan pertama yang dihadapi pengembang penerjemah bahasa yang memungkinkan kelebihan prosedur dan fungsi adalah bagaimana memilih di antara prosedur dengan nama yang sama yang harus diterapkan dalam suatu kasus tertentu? Semuanya baik-baik saja jika ada varian prosedur, jenis parameter formal yang sama persis dengan jenis parameter aktual yang digunakan dalam panggilan ini. Namun, di hampir semua bahasa, ada tingkat kebebasan tertentu dalam penggunaan tipe, dengan asumsi bahwa kompiler secara otomatis melakukan konversi tipe-aman dalam situasi tertentu. Misalnya, dalam operasi aritmatika pada argumen real dan integer, integer biasanya diubah menjadi tipe real secara otomatis, dan hasilnya adalah real. Misalkan ada dua varian fungsi add:

int tambah(int a1, int a2); float add(float a1, float a2);

Bagaimana seharusnya kompiler menangani ekspresi y = add(x, i) di mana x adalah float dan i adalah int? Jelas tidak ada kecocokan yang tepat. Ada dua opsi: baik y=add_int((int)x,i) , atau sebagai y=add_flt(x, (float)i) (di sini nama add_int dan add_float masing-masing menunjukkan versi pertama dan kedua dari fungsi tersebut) .

Timbul pertanyaan: haruskah kompiler mengizinkan penggunaan fungsi yang kelebihan beban ini, dan jika demikian, atas dasar apa ia akan memilih varian tertentu yang digunakan? Secara khusus, dalam contoh di atas, haruskah penerjemah mempertimbangkan jenis variabel y saat memilih? Perlu dicatat bahwa situasi di atas adalah yang paling sederhana, kasus yang jauh lebih rumit dimungkinkan, yang diperburuk oleh fakta bahwa tidak hanya tipe bawaan yang dapat diubah sesuai dengan aturan bahasa, tetapi juga kelas yang dideklarasikan oleh pemrogram. , jika mereka memiliki hubungan kekerabatan, dapat dilempar satu sama lain. Ada dua solusi untuk masalah ini:

  • Larang identifikasi yang tidak akurat sama sekali. Mengharuskan bahwa untuk setiap pasangan jenis tertentu ada varian yang tepat dari prosedur atau operasi kelebihan beban. Jika tidak ada opsi seperti itu, kompiler harus melakukan kesalahan. Pemrogram dalam hal ini harus menerapkan konversi eksplisit untuk menampilkan parameter aktual ke kumpulan tipe yang diinginkan. Pendekatan ini tidak nyaman dalam bahasa seperti C++, yang memungkinkan kebebasan yang cukup dalam menangani tipe, karena ini mengarah pada perbedaan yang signifikan dalam perilaku operator bawaan dan kelebihan beban (operasi aritmatika dapat diterapkan pada bilangan biasa tanpa berpikir, tetapi untuk tipe lain - hanya dengan konversi eksplisit) atau munculnya sejumlah besar opsi untuk operasi.
  • Tetapkan aturan tertentu untuk memilih "kecocokan terdekat". Biasanya, dalam varian ini, kompiler memilih varian yang panggilannya dapat diperoleh dari sumber hanya dengan konversi jenis aman (non-lossy information), dan jika ada beberapa di antaranya, ia dapat memilih berdasarkan varian mana yang membutuhkan lebih sedikit konversi seperti itu. Jika hasilnya menyisakan lebih dari satu kemungkinan, kompiler melontarkan kesalahan dan mengharuskan pemrogram untuk menentukan varian secara eksplisit.

Operasi Overloading Pertimbangan Khusus

Tidak seperti prosedur dan fungsi, operasi infiks bahasa pemrograman memiliki dua properti tambahan yang secara signifikan memengaruhi fungsinya: prioritas dan asosiatif, yang keberadaannya disebabkan oleh kemungkinan perekaman "rantai" operator (cara memahami a + b * c: as (a + b )*c or like a+(b*c) ? Ekspresi a-b+c adalah (a-b)+c atau a-(b+c) ?).

Operasi yang dibangun ke dalam bahasa selalu memiliki prioritas dan asosiatif tradisional yang telah ditentukan sebelumnya. Timbul pertanyaan: prioritas dan asosiatif apa yang akan dimiliki oleh versi yang didefinisikan ulang dari operasi ini, atau, terlebih lagi, operasi baru yang dibuat oleh programmer? Ada seluk-beluk lain yang mungkin memerlukan klarifikasi. Misalnya, di C ada dua bentuk operator kenaikan dan penurunan ++ dan -- - awalan dan akhiran, yang berperilaku berbeda. Bagaimana seharusnya versi kelebihan beban dari operator tersebut berperilaku?

Bahasa yang berbeda menangani masalah ini dengan cara yang berbeda. Jadi, dalam C++, prioritas dan asosiatif dari versi operator yang kelebihan beban tetap sama seperti yang didefinisikan dalam bahasa; adalah mungkin untuk secara terpisah membebani bentuk awalan dan akhiran dari operator kenaikan dan penurunan menggunakan tanda tangan khusus:

Jadi int digunakan untuk membuat perbedaan pada tanda tangan

Pengumuman operasi baru

Situasi dengan pengumuman operasi baru bahkan lebih rumit. Tidaklah sulit untuk memasukkan kemungkinan deklarasi semacam itu ke dalam bahasa, tetapi implementasinya penuh dengan kesulitan yang signifikan. Mendeklarasikan operasi baru sebenarnya adalah membuat yang baru kata kunci bahasa pemrograman, diperumit oleh fakta bahwa operasi dalam teks, sebagai suatu peraturan, dapat mengikuti tanpa pemisah dengan token lain. Ketika muncul, kesulitan tambahan muncul dalam pengorganisasian penganalisis leksikal. Misalnya, jika bahasa sudah memiliki operasi "+" dan unary "-" (perubahan tanda), maka ekspresi a+-b dapat secara akurat diartikan sebagai a + (-b) , tetapi jika operasi baru +- dideklarasikan dalam program, segera timbul ambiguitas, karena ekspresi yang sama sudah dapat diuraikan sebagai a (+-) b . Pengembang dan pelaksana bahasa harus menangani masalah seperti itu dengan cara tertentu. Opsinya, sekali lagi, bisa berbeda: mengharuskan semua operasi baru menjadi karakter tunggal, mendalilkan bahwa jika ada ketidaksesuaian, versi operasi "terpanjang" dipilih (yaitu, hingga kumpulan karakter berikutnya dibaca oleh penerjemah cocok dengan operasi apa pun, itu terus dibaca), mencoba mendeteksi tabrakan selama terjemahan dan menghasilkan kesalahan dalam kasus kontroversial ... Dengan satu atau lain cara, bahasa yang memungkinkan deklarasi operasi baru memecahkan masalah ini.

Tidak boleh dilupakan bahwa untuk operasi baru juga ada masalah penentuan asosiatif dan prioritas. Tidak ada lagi solusi siap pakai dalam bentuk operasi bahasa standar, dan biasanya Anda hanya perlu mengatur parameter ini dengan aturan bahasa. Misalnya, buat semua operasi baru menjadi asosiatif kiri dan beri mereka prioritas yang sama, tetap, atau masukkan ke dalam bahasa cara untuk menentukan keduanya.

Overloading dan variabel polimorfik

Ketika operator, fungsi, dan prosedur kelebihan beban digunakan dalam bahasa yang diketik dengan kuat, di mana setiap variabel memiliki tipe yang telah dideklarasikan sebelumnya, terserah kompiler untuk memutuskan versi mana dari operator kelebihan beban yang akan digunakan dalam setiap kasus tertentu, tidak peduli seberapa rumitnya . Ini berarti bahwa untuk bahasa yang dikompilasi, penggunaan kelebihan operator tidak menyebabkan penurunan kinerja - bagaimanapun juga, ada operasi atau panggilan fungsi yang terdefinisi dengan baik dalam kode objek program. Situasinya berbeda ketika dimungkinkan untuk menggunakan variabel polimorfik dalam bahasa, yaitu variabel yang dapat berisi nilai dari jenis yang berbeda pada waktu yang berbeda.

Karena jenis nilai yang akan diterapkan oleh operasi beban berlebih tidak diketahui pada saat penerjemahan kode, kompiler tidak dapat memilih pilihan yang diinginkan di muka. Dalam hal ini, ia terpaksa menyematkan sebuah fragmen ke dalam kode objek yang, tepat sebelum melakukan operasi ini, akan menentukan jenis nilai dalam argumen dan secara dinamis memilih varian yang sesuai dengan kumpulan jenis ini. Selain itu, definisi seperti itu harus dibuat pada setiap eksekusi operasi, karena bahkan kode yang sama, yang dipanggil untuk kedua kalinya, dapat dieksekusi secara berbeda.

Dengan demikian, penggunaan kelebihan beban operator dalam kombinasi dengan variabel polimorfik membuatnya tak terelakkan untuk secara dinamis menentukan kode mana yang akan dipanggil.

Kritik

Penggunaan yang berlebihan tidak dianggap sebagai anugerah oleh semua ahli. Jika kelebihan beban fungsi dan prosedur umumnya tidak dapat diterima (sebagian karena tidak menyebabkan beberapa masalah "operator" yang khas, sebagian karena kurang tergoda untuk menyalahgunakannya), maka kelebihan beban operator, pada prinsipnya, , dan dalam implementasi bahasa tertentu, mengalami kritik yang cukup parah dari banyak ahli teori dan praktisi pemrograman.

Kritik menunjukkan bahwa masalah identifikasi, preseden, dan asosiatif yang diuraikan di atas sering membuat berurusan dengan operator yang kelebihan beban menjadi sulit atau tidak wajar:

  • Identifikasi. Jika bahasa memiliki aturan identifikasi yang ketat, maka pemrogram dipaksa untuk mengingat kombinasi tipe mana yang memiliki operasi kelebihan beban dan secara manual memasukkan operan ke dalamnya. Jika bahasa memungkinkan identifikasi "perkiraan", orang tidak pernah dapat memastikan bahwa dalam beberapa situasi yang agak rumit, varian operasi yang ada dalam pikiran programmer akan dilakukan.
  • Prioritas dan asosiatif. Jika didefinisikan secara kaku, ini mungkin merepotkan dan tidak relevan dengan bidang subjek (misalnya, untuk operasi dengan himpunan, prioritas berbeda dari operasi aritmatika). Jika mereka dapat diatur oleh pemrogram, ini menjadi sumber kesalahan tambahan (jika hanya karena varian yang berbeda dari satu operasi ternyata memiliki prioritas yang berbeda, atau bahkan asosiatif).

Seberapa besar kenyamanan menggunakan operasi Anda sendiri dapat melebihi ketidaknyamanan akibat memburuknya kemampuan kontrol program adalah pertanyaan yang tidak memiliki jawaban yang jelas.

Dari sudut pandang implementasi bahasa, masalah yang sama mengarah pada kompleksitas penerjemah dan penurunan efisiensi dan keandalannya. Dan penggunaan kelebihan dalam hubungannya dengan variabel polimorfik juga jelas lebih lambat daripada memanggil operasi hardcode selama kompilasi, dan memberikan lebih sedikit peluang untuk mengoptimalkan kode objek. Ciri-ciri khusus penerapan kelebihan beban dalam berbagai bahasa mendapat kritik tersendiri. Jadi, dalam C++, objek kritik dapat berupa kurangnya kesepakatan tentang representasi internal dari nama-nama fungsi yang kelebihan beban, yang menimbulkan ketidakcocokan pada tingkat pustaka yang dikompilasi oleh kompiler C++ yang berbeda.

Beberapa kritikus menentang operasi kelebihan muatan, berdasarkan prinsip-prinsip umum teori pembangunan perangkat lunak dan praktik industri nyata.

  • Pendukung pendekatan "puritan" terhadap pembangunan bahasa, seperti Wirth atau Hoare, menentang kelebihan beban operator hanya karena hal itu dapat dengan mudah disingkirkan. Menurut mereka, alat seperti itu hanya memperumit bahasa dan penerjemah, tanpa menyediakan komplikasi yang sesuai fitur tambahan. Menurut pendapat mereka, gagasan untuk membuat perluasan bahasa yang berorientasi pada tugas hanya terlihat menarik. Pada kenyataannya, penggunaan alat ekstensi bahasa membuat program hanya dapat dipahami oleh pembuatnya - orang yang mengembangkan ekstensi ini. Program menjadi jauh lebih sulit bagi pemrogram lain untuk memahami dan menganalisis, membuat pemeliharaan, modifikasi, dan pengembangan tim menjadi lebih sulit.
  • Perlu dicatat bahwa kemungkinan penggunaan kelebihan sering memainkan peran provokatif: pemrogram mulai menggunakannya sedapat mungkin, akibatnya, alat yang dirancang untuk menyederhanakan dan merampingkan program menjadi penyebab kerumitan dan kebingungannya.
  • Operator yang kelebihan beban mungkin tidak melakukan persis seperti yang diharapkan dari mereka, berdasarkan jenisnya. Misalnya, a + b biasanya (tetapi tidak selalu) memiliki arti yang sama dengan b + a , tetapi "satu" + "dua" berbeda dari "dua" + "satu" dalam bahasa di mana + operator kelebihan beban untuk rangkaian string.
  • Kelebihan operator membuat fragmen program lebih peka konteks. Tanpa mengetahui jenis operan yang terlibat dalam ekspresi, tidak mungkin untuk memahami apa yang dilakukan ekspresi jika menggunakan operator yang kelebihan beban. Misalnya, dalam program C++, pernyataan<< может означать и побитовый сдвиг, и вывод в поток. Выражение a << 1 возвращает результат побитового сдвига значения a на один бит влево, если a - целая переменная, но если a является выходным потоком , то же выражение выведет в этот поток строку «1» .

Klasifikasi

Berikut ini adalah klasifikasi beberapa bahasa pemrograman berdasarkan apakah mereka mengizinkan kelebihan beban operator, dan apakah operator terbatas pada set yang telah ditentukan sebelumnya:

Operasi Tidak ada kelebihan Ada kelebihan beban
Serangkaian operasi terbatas
  • Objective-C
  • Piton
Dimungkinkan untuk mendefinisikan operasi baru
  • PostgreSQL
  • Lihat juga

    Yayasan Wikimedia. 2010 .

    Lihat apa itu "Function Overloading" di kamus lain:

      - (operator, fungsi, prosedur) dalam pemrograman salah satu cara untuk mengimplementasikan polimorfisme, yang terdiri dari kemungkinan keberadaan simultan dalam satu lingkup dari beberapa varian operasi yang berbeda (operator, fungsi atau ... ... Wikipedia

Overload fungsi adalah definisi dari beberapa fungsi (dua atau lebih) dengan nama yang sama tetapi parameter yang berbeda. Kumpulan parameter fungsi kelebihan beban mungkin berbeda dalam urutan, nomor, dan jenis. Oleh karena itu, kelebihan fungsi diperlukan untuk menghindari duplikasi nama fungsi yang melakukan tindakan serupa, tetapi dengan logika program yang berbeda. Misalnya, pertimbangkan fungsi areaRectangle() , yang menghitung luas persegi panjang.

Float areaRectangle(float, float) //fungsi yang menghitung luas persegi panjang dengan dua parameter a(cm) dan b(cm) ( return a * b; // gandakan panjang sisi persegi panjang dan kembalikan produk yang dihasilkan)

Jadi, ini adalah fungsi dengan dua parameter tipe float, dan argumen yang diteruskan ke fungsi harus dalam sentimeter, nilai pengembalian tipe float juga dalam sentimeter.

Misalkan data awal kita (sisi persegi panjang) diberikan dalam meter dan sentimeter, misalnya: a = 2m 35 cm; b = 1m 86 cm Dalam hal ini, akan lebih mudah menggunakan fungsi dengan empat parameter. Artinya, setiap panjang sisi persegi panjang diteruskan ke fungsi dalam dua parameter: meter dan sentimeter.

Luas apung Persegi Panjang(float a_m, float a_sm, float b_m, float b_sm) // fungsi yang menghitung luas persegi panjang dengan 4 parameter a(m) a(cm); b(m) b(cm) ( kembali (a_m * 100 + a_sm) * (b_m * 100 + b_sm); )

Di badan fungsi, nilai yang diteruskan dalam meter (a_m dan b_m) diubah menjadi sentimeter dan ditambahkan ke nilai a_sm b_sm , setelah itu kita mengalikan jumlahnya dan mendapatkan luas persegi panjang dalam cm Tentu saja, dimungkinkan untuk mengubah data asli menjadi sentimeter dan menggunakan fungsi pertama, tetapi sekarang bukan tentang itu.

Sekarang, yang terpenting adalah kita memiliki dua fungsi, dengan tanda tangan berbeda, tetapi nama yang sama (fungsi kelebihan beban). Tanda tangan adalah kombinasi nama fungsi dengan parameternya. Bagaimana cara memanggil fungsi-fungsi ini? Dan memanggil fungsi yang kelebihan beban tidak berbeda dengan memanggil fungsi biasa, misalnya:

Persegi Panjang(32, 43); // sebuah fungsi akan dipanggil yang menghitung luas persegi panjang dengan dua parameter a(cm) dan b(cm) areaRectangle(4, 43, 2, 12); // sebuah fungsi akan dipanggil yang menghitung luas persegi panjang dengan 4 parameter a(m) a(cm); b(m) b(cm)

Seperti yang Anda lihat, kompiler akan secara mandiri memilih fungsi yang diinginkan, hanya menganalisis tanda tangan dari fungsi yang kelebihan beban. Melewati kelebihan fungsi, seseorang dapat dengan mudah mendeklarasikan fungsi dengan nama yang berbeda, dan itu akan melakukan tugasnya dengan baik. Tetapi bayangkan apa yang akan terjadi jika Anda membutuhkan lebih dari dua fungsi seperti itu, misalnya 10. Dan untuk masing-masing fungsi tersebut Anda perlu membuat nama yang bermakna, dan hal yang paling sulit untuk diingat adalah fungsi tersebut. Itulah mengapa lebih mudah dan lebih baik untuk membebani fungsi, kecuali tentu saja ada kebutuhan untuk ini. Kode sumber program ditunjukkan di bawah ini.

#include "stdafx.h" #include << "S1 = " << areaRectangle(32,43) << endl; // вызов перегруженной функции 1 cout << "S2 = " << areaRectangle(4, 43, 2, 12) << endl; // вызов перегруженной функции 2 return 0; } // перегруженная функция 1 float areaRectangle(float a, float b) //функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см) { return a * b; // умножаем длинны сторон прямоугольника и возвращаем полученное произведение } // перегруженная функция 2 float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм) { return (a_m * 100 + a_sm) * (b_m * 100 + b_sm); }

// kode Kode::Blok

// kode Dev-C++

#termasuk menggunakan namespace std; // prototipe fungsi kelebihan beban float areaRectangle(float a, float b); float areaRectangle(float a_m, float a_sm, float b_m, float b_sm); int main() ( cout<< "S1 = " << areaRectangle(32,43) << endl; // вызов перегруженной функции 1 cout << "S2 = " << areaRectangle(4, 43, 2, 12) << endl; // вызов перегруженной функции 2 return 0; } // перегруженная функция 1 float areaRectangle(float a, float b) //функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см) { return a * b; // умножаем длинны сторон прямоугольника и возвращаем полученное произведение } // перегруженная функция 2 float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм) { return (a_m * 100 + a_sm) * (b_m * 100 + b_sm); }

Hasil program ditunjukkan pada Gambar 1.



Bagaimana cara mencapai kelebihan fungsi di C? (10)

Apakah ada cara untuk mencapai kelebihan fungsi di C? Saya sedang melihat fungsi sederhana yang bisa kelebihan beban seperti

foo (int a) foo (char b) foo (float c , int d)

Saya pikir tidak ada cara langsung; Saya mencari solusi, jika ada.

Saya harap kode di bawah ini akan membantu Anda memahami kelebihan fungsi

#termasuk #termasuk int menyenangkan(int a, ...); int main(int argc, char *argv)( fun(1,10); fun(2,"cquestionbank"); return 0; ) int fun(int a, ...)( va_list vl; va_start(vl,a ); if(a==1) printf("%d",va_arg(vl,int)); selain itu printf("\n%s",va_arg(vl,char *)); )

Maksud saya, maksud Anda - tidak, Anda tidak bisa.

Anda dapat mendeklarasikan fungsi va_arg sebagai

batal fungsi_saya(format char*, ...);

Tetapi Anda harus memberikan beberapa informasi tentang jumlah variabel dan tipenya di argumen pertama - seperti printf() .

Ya suka.

Di sini Anda memberi contoh:

void printA(int a)( printf("Halo dunia dari printA: %d\n",a); ) void printB(const char *buff)( printf("Halo dunia dari printB: %s\n",buff) ; ) #define Max_ITEMS() 6, 5, 4, 3, 2, 1, 0 #define __VA_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N #define _Num_ARGS_(... ) __VA_ARG_N(__VA_ARGS__) #define NUM_ARGS(...) (_Num_ARGS_(_0, ## __VA_ARGS__, Max_ITEMS()) - 1) #define CHECK_ARGS_MAX_LIMIT(t) if(NUM_ARGS(args)>t) #define CHECK_ARGS_MIN_LIMIT(t) if(NUM_ARGS(args) #define print(x , args ...) \ CHECK_ARGS_MIN_LIMIT(1) printf("error");fflush(stdout); \ CHECK_ARGS_MAX_LIMIT(4) printf("error");fflush(stdout) ; \ (( \ if (__builtin_types_compatible_p (typeof (x), int)) \ printA(x, ##args); \ else \ printB (x,##args); \ )) int main(int argc, char* * argv) ( int a=0; print(a); print("hello"); return (EXIT_SUCCESS); )

Ini akan menampilkan 0 dan halo dari printA dan printB.

Jika kompiler Anda adalah gcc dan Anda tidak keberatan melakukan pembaruan manual setiap kali Anda menambahkan kelebihan baru, Anda dapat membuat massa makro dan mendapatkan hasil yang Anda inginkan dari sudut pandang penelepon, tidak begitu baik untuk ditulis ... tetapi itu mungkin

lihat __builtin_types_compatible_p lalu gunakan untuk mendefinisikan makro yang melakukan sesuatu seperti

#define foo(a) \ ((__builtin_types_kompatibel_p(int, a)?foo(a):(__builtin_types_kompatibel_p(float, a)?foo(a):)

tapi ya, jahat, tidak

EDIT: C1X akan mendapatkan dukungan untuk ekspresi tipe, yang terlihat seperti ini:

#define cbrt(X) _Generic((X), panjang ganda: cbrtl, \ default: cbrt, \ float: cbrtf)(X)

Seperti yang sudah disebutkan, kelebihan dalam arti yang Anda maksud tidak didukung oleh C. Ungkapan yang biasa digunakan untuk menyelesaikan masalah adalah untuk fungsi mengambil tagged union . Ini diimplementasikan menggunakan parameter struct, di mana struct itu sendiri terdiri dari beberapa jenis indikator tipe, seperti enum , dan gabungan dari tipe nilai yang berbeda. Contoh:

#termasuk typedef enum ( T_INT, T_FLOAT, T_CHAR, ) tipe_saya; typedef struct ( my_type type; union ( int a; float b; char c; ) my_union; ) my_struct; void set_overload (my_struct *terserah) ( switch (terserah->tipe) ( case T_INT: apapun->my_union.a = 1; break; case T_FLOAT: apapun->my_union.b = 2.0; break; case T_CHAR: apapun-> my_union.c = "3"; ) ) void printf_overload (my_struct *terserah) ( switch (apapun->type) ( case T_INT: printf("%d\n", apapun->my_union.a); break; case T_FLOAT : printf("%f\n", apapun->my_union.b); break; case T_CHAR: printf("%c\n", apapun->my_union.c); break; ) ) int main (int argc, char* argv) ( my_struct s; s.type=T_INT; set_overload(&s); printf_overload(&s); s.type=T_FLOAT; set_overload(&s); printf_overload(&s); s.type=T_CHAR; set_overload(&s) ; printf_overload(&s); )

Tidak bisakah Anda menggunakan C++ saja dan tidak menggunakan semua fitur C++ selain yang ini?

Jika sejauh ini belum ada C yang ketat, saya akan merekomendasikan fungsi variadic sebagai gantinya.

Pendekatan berikut mirip dengan a2800276, tetapi dengan beberapa makro C99:

// kita membutuhkan `size_t` #include // tipe argumen untuk menerima enum sum_arg_types ( SUM_LONG, SUM_ULONG, SUM_DOUBLE ); // struktur untuk menampung argumen struct sum_arg ( enum sum_arg_types type; union ( long as_long; unsigned long as_ulong; double as_double; ) value; ); // tentukan ukuran array #define count(ARRAY) ((sizeof (ARRAY))/(sizeof *(ARRAY))) // beginilah fungsi kita akan dipanggil #define sum(...) _sum( count(sum_args(__VA_ARGS__)), sum_args(__VA_ARGS__)) // buat array `struct sum_arg` #define sum_args(...) ((struct sum_arg )( __VA_ARGS__ )) // buat penginisialisasi untuk argumen #define sum_long (VALUE) ( SUM_LONG, ( .as_long = (VALUE) ) ) #define sum_ulong(VALUE) ( SUM_ULONG, ( .as_ulong = (VALUE) ) ) #define sum_double(VALUE) ( SUM_DOUBLE, ( .as_double = (VALUE) ) ) // fungsi polimorfik kita long double _sum(size_t count, struct sum_arg * args) ( long double value = 0; for(size_t i = 0; i< count; ++i) { switch(args[i].type) { case SUM_LONG: value += args[i].value.as_long; break; case SUM_ULONG: value += args[i].value.as_ulong; break; case SUM_DOUBLE: value += args[i].value.as_double; break; } } return value; } // let"s see if it works #include int main() ( unsigned long foo = -1; long double value = sum(sum_long(42), sum_ulong(foo), sum_double(1e10)); printf("%Le\n", nilai); return 0; )

Untuk saat ini, _Generic karena pertanyaan _Generic, C standar (tanpa ekstensi) efektif diterima dukungan untuk fungsi overloading (bukan operator) berkat penambahan kata _Generic _Generic di C11. (didukung di GCC sejak versi 4.9)

(Pembebanan berlebih tidak benar-benar "bawaan" seperti yang ditunjukkan dalam pertanyaan, tetapi mudah untuk menghancurkan sesuatu yang berfungsi seperti ini.)

Generik adalah operator waktu kompilasi dalam keluarga yang sama dengan sizeof dan _Alignof . Ini dijelaskan dalam bagian standar 6.5.1.1. Dibutuhkan dua parameter utama: ekspresi (yang tidak akan dievaluasi saat runtime) dan daftar asosiasi tipe/ekspresi, yang sedikit mirip dengan blok sakelar. _Generic mendapatkan tipe generik dari ekspresi dan kemudian "beralih" ke ekspresi itu untuk memilih ekspresi hasil akhir dalam daftar untuk tipenya:

Generik(1, float: 2.0, char *: "2", int: 2, default: get_two_object());

Ekspresi di atas dievaluasi menjadi 2 - tipe dari ekspresi kontrol adalah int , jadi ia memilih ekspresi yang terkait dengan int sebagai nilainya. Tidak ada yang tersisa saat runtime. (Klausul default adalah wajib: jika Anda tidak menentukannya dan jenisnya tidak cocok, ini akan menyebabkan kesalahan kompilasi.)

Teknik yang berguna untuk kelebihan beban fungsi adalah dapat disisipkan oleh preprosesor C dan memilih ekspresi hasil berdasarkan jenis argumen yang diteruskan ke makro pengontrol. Jadi (contoh dari standar C):

#define cbrt(X) _Generic((X), \ long double: cbrtl, \ default: cbrt, \ float: cbrtf \)(X)

Makro ini mengimplementasikan operasi cbrt yang kelebihan beban dengan meneruskan jenis argumen ke makro, memilih fungsi implementasi yang sesuai, lalu meneruskan makro asli ke fungsi tersebut.

Jadi, untuk mengimplementasikan contoh asli Anda, kami dapat melakukan ini:

Foo_int (int a) foo_char (char b) foo_float_int (float c , int d) #define foo(_1, ...) _Generic((_1), \ int: foo_int, \ char: foo_char, \ float: _Generic(( PERTAMA(__VA_ARGS__,)), \int: foo_float_int))(_1, __VA_ARGS__) #define PERTAMA(A, ...) A

Dalam kasus ini, kita dapat menggunakan default: binding untuk kasus ketiga, tetapi itu tidak menunjukkan cara memperluas prinsip ke beberapa argumen. Hasil akhirnya adalah Anda dapat menggunakan foo(...) dalam kode Anda tanpa mengkhawatirkan (banyak) jenis argumen Anda.

Untuk situasi yang lebih kompleks, seperti fungsi yang membebani lebih banyak argumen atau mengubah angka, Anda dapat menggunakan makro utilitas untuk membuat struktur pengiriman statis secara otomatis:

batal print_ii(int a, int b) ( printf("int, int\n"); ) batal print_di(ganda a, int b) ( printf("ganda, int\n"); ) batal print_iii(int a, int b, int c) ( printf("int, int, int\n"); ) void print_default(void) ( printf("argumen tidak diketahui\n"); ) #define print(...) OVERLOAD(print, (__VA_ARGS__), \ (print_ii, (int, int)), \ (print_di, (double, int)), \ (print_iii, (int, int, int)) \) #define OVERLOAD_ARG_TYPES (int, double) #define OVERLOAD_FUNCTIONS (print) #include "activate-overloads.h" int main(void) ( print(44, 47); // print "int, int" print(4.4, 47); // print "double, int" print (1, 2, 3); // mencetak "int, int, int" print(""); // mencetak "argumen tidak diketahui" )

(implementasi di sini). Dengan sedikit usaha, Anda dapat mengurangi boilerplate agar terlihat sangat mirip dengan bahasa dengan dukungan bawaan yang berlebihan.

Selain itu, sudah mungkin kelebihan beban kuantitas argumen (bukan tipe) di C99.

Perhatikan bahwa cara C dievaluasi dapat menggerakkan Anda. Ini akan memilih foo_int jika Anda mencoba meneruskan karakter literal ke sana, misalnya, dan Anda memerlukan beberapa foo_int jika Anda ingin kelebihan Anda mendukung literal string. Namun, secara keseluruhan cukup keren.

Jawaban Leushenko sangat keren: hanya contoh foo yang tidak dapat dikompilasi dengan GCC, yang gagal foo(7) , menabrak makro FIRST dan panggilan fungsi sebenarnya ((_1, __VA_ARGS__) , tersisa dengan koma tambahan. Juga, kami mengalami masalah, jika kami ingin memberikan kelebihan tambahan seperti foo(double) .

Jadi saya memutuskan untuk menjawab pertanyaan ini secara lebih rinci, termasuk mengizinkan kelebihan beban kosong (foo(void) - yang menyebabkan beberapa masalah...).

Idenya sekarang adalah: tentukan lebih dari satu generik dalam makro yang berbeda dan biarkan yang benar dipilih sesuai dengan jumlah argumen!

Jumlah argumen cukup sederhana, berdasarkan jawaban ini:

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__) #define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__) #define CONCAT(X, Y) CONCAT_(X, Y) # tentukan CONCAT_(X, Y) X ## Y

Itu bagus, kami memutuskan antara SELECT_1 atau SELECT_2 (atau lebih banyak argumen jika Anda menginginkan/membutuhkannya), jadi kami hanya memerlukan definisi yang sesuai:

#define SELECT_0() foo_void #define SELECT_1(_1) _Generic ((_1), \ int: foo_int, \ char: foo_char, \ double: foo_double \) #define SELECT_2(_1, _2) _Generic((_1), \ double : _Generik((_2), \ int: foo_double_int \) \)

Pertama, panggilan makro kosong (foo()) masih membuat token, tetapi kosong. Jadi makro hitung sebenarnya mengembalikan 1, bukan 0, meskipun makro disebut kosong. Kita dapat "dengan mudah" memperbaiki masalah ini jika kita __VA_ARGS__ dengan koma setelah __VA_ARGS__ secara kondisional, tergantung pada apakah daftar itu kosong atau tidak:

#define NARG(...) ARG4_(__VA_ARGS__ COMMA(__VA_ARGS__) 4, 3, 2, 1, 0)

Ini tampak mudah, tetapi makro COMMA cukup berat; untungnya, topik ini sudah dibahas di blog Jens Gustedt (terima kasih, Jens). Trik utamanya adalah bahwa makro fungsi tidak berkembang kecuali diikuti oleh tanda kurung, lihat blog Jens untuk penjelasan lebih lanjut... Kita hanya perlu sedikit memodifikasi makro untuk kebutuhan kita (saya akan menggunakan nama yang lebih pendek dan lebih sedikit argumen untuk singkatnya) .

#define ARGN(...) ARGN_(__VA_ARGS__) #define ARGN_(_0, _1, _2, _3, N, ...) N #define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0 ) #define SET_COMMA(...) , #define COMMA(...) SELECT_COMMA \ (\ HAS_COMMA(__VA_ARGS__), \ HAS_COMMA(__VA_ARGS__ ()), \ HAS_COMMA(SET_COMMA __VA_ARGS__), \ HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \) #define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3) #define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3 # tentukan COMMA_0000 , #define COMMA_0001 #define COMMA_0010 , // ... (semua lainnya dengan koma) #define COMMA_1111 ,

Dan sekarang kita baik-baik saja...

Kode lengkap dalam satu blok:

/* * demo.c * * Dibuat pada: 14-09-2017 * Penulis: sboehler */ #include batal foo_void(void) ( menempatkan("void"); ) batal foo_int(int c) ( printf("int: %d\n", c); ) batal foo_char(char c) ( printf("char: %c \n", c); ) batal foo_double(ganda c) ( printf("ganda: %.2f\n", c); ) batal foo_double_int(ganda c, int d) ( printf("ganda: %.2f, int: %d\n", c, d); ) #define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__) #define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__) # tentukan CONCAT(X, Y) CONCAT_(X, Y) #define CONCAT_(X, Y) X ## Y #define SELECT_0() foo_void #define SELECT_1(_1) _Generic ((_1), \ int: foo_int, \ char : foo_char, \ ganda: foo_double \) #define SELECT_2(_1, _2) _Generic((_1), \ ganda: _Generic((_2), \ int: foo_double_int \) \) #define ARGN(...) ARGN_( __VA_ARGS__) #define ARGN_(_0, _1, _2, N, ...) N #define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0) #define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0) #define SET_COMMA(...) , #define COMMA(...) SELECT_COMMA \ (\ HAS_COMMA(__VA_ARGS__), \ HAS_COMMA(__VA_ARGS__ ()), \ HAS_C OMMA(SET_COMMA __VA_ARGS__), \ HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \) #define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3) #define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3 COMMA_1001 , #define COMMA_1010 , #define COMMA_1011 , #define COMMA_1100 , #define COMMA_1101 , #define COMMA_1110 , #define COMMA_11**1 , int main(int argc) ( foo(); foo(7); foo(10.12); foo(12.10, 7); foo((char)"s"); kembali 0; )

Anotasi: Kuliah membahas konsep, deklarasi dan penggunaan fungsi inline dan overload pada program C++, mekanisme melakukan substitusi dan overloading fungsi, rekomendasi untuk meningkatkan efisiensi program melalui overloading atau substitusi fungsi.

Tujuan dari kuliah: mempelajari fungsi sebaris (tertanam) dan kelebihan fungsi, mempelajari cara mengembangkan program menggunakan kelebihan fungsi di C ++.

Fungsi sebaris

Memanggil fungsi, meneruskan nilai ke dalamnya, mengembalikan nilai - operasi ini memakan banyak waktu CPU. Biasanya, saat mendefinisikan suatu fungsi, kompiler hanya menyimpan satu blok sel di memori untuk menyimpan pernyataannya. Setelah fungsi dipanggil, kontrol program ditransfer ke operator ini, dan setelah kembali dari fungsi, eksekusi program dilanjutkan dari baris setelah pemanggilan fungsi.

Dengan panggilan berulang, setiap kali program akan memproses kumpulan perintah yang sama, tanpa membuat salinan untuk setiap panggilan secara terpisah.

Setiap lompatan ke area memori yang berisi pernyataan fungsi memperlambat eksekusi program. Jika fungsinya kecil, Anda dapat menghemat waktu pada banyak panggilan dengan menginstruksikan kompiler untuk menyisipkan kode fungsi langsung ke dalam program di situs panggilan. Fungsi seperti itu disebut diganti. Dalam hal ini, berbicara tentang efisiensi, pertama-tama tersirat kecepatan eksekusi program.

Fungsi Inline atau Inline adalah fungsi yang kodenya disisipkan oleh kompiler langsung di situs panggilan, alih-alih mentransfer kontrol ke satu instance fungsi.

Jika fungsinya sebaris, kompiler tidak membuat fungsi yang diberikan di memori, tetapi menyalin stringnya langsung ke kode program di tempat pemanggilan. Ini setara dengan menulis blok yang sesuai dalam program alih-alih pemanggilan fungsi. Jadi penentu Di barisan mendefinisikan untuk fungsi yang disebut pengikatan batin, yang terletak pada fakta bahwa kompiler mengganti perintah dari kodenya alih-alih memanggil fungsinya. Fungsi sebaris digunakan jika badan fungsi terdiri dari beberapa pernyataan.

Pendekatan ini memungkinkan Anda meningkatkan kecepatan eksekusi program, karena perintah dikecualikan dari program. mikroprosesor Diperlukan untuk meneruskan argumen dan memanggil fungsi.

Misalnya:

/*fungsi mengembalikan jarak dari titik dengan koordinat(x1,y1) ke titik dengan koordinat (x2,y2)*/ inline float Line(float x1,float y1,float x2, float y2) ( return sqrt(pow(x1-x2) ,2)+pow(y1-y2,2)); )

Namun, perlu dicatat bahwa penggunaan fungsi sebaris tidak selalu memberikan efek positif. Jika fungsi seperti itu dipanggil beberapa kali dalam kode program, maka masuk waktu kompilasi salinan fungsi ini sebanyak jumlah panggilannya akan dimasukkan ke dalam program. Akan ada peningkatan yang signifikan dalam ukuran kode program, akibatnya peningkatan efisiensi eksekusi program yang diharapkan mungkin tidak terjadi.

Contoh 1.

#include "stdafx.h" #include menggunakan namespace std; kubus int sebaris (int x); int _tmain(int argc, _TCHAR* argv)( int x=2; float y=3; double z=4; cout<

Kami mencantumkan alasan mengapa fungsi dengan specifier inline akan diperlakukan sebagai fungsi non-inline biasa:

  • fungsi inline bersifat rekursif;
  • fungsi yang pemanggilannya ditempatkan sebelum definisinya;
  • fungsi yang dipanggil lebih dari satu kali dalam sebuah ekspresi;
  • fungsi yang mengandung loop, switch, dan operator lompat;
  • fungsi yang terlalu besar untuk memungkinkan substitusi.

Pembatasan dalam melakukan substitusi sebagian besar bergantung pada implementasi. Jika untuk fungsi dengan specifier Di barisan kompiler tidak dapat melakukan substitusi karena konteks di mana panggilan ditempatkan, maka fungsi tersebut dianggap statis dan dikeluarkan pesan peringatan.

Fitur lain dari fungsi inline adalah ketidakmungkinan untuk mengubahnya tanpa mengkompilasi ulang semua bagian program yang memanggil fungsi ini.

Kelebihan fungsi

Saat mendefinisikan fungsi dalam program, perlu untuk menentukan jenis nilai yang dikembalikan oleh fungsi, serta jumlah parameter dan jenis masing-masingnya. Jika ada fungsi di C++ yang disebut add_values ​​​​yang beroperasi pada dua nilai bilangan bulat, dan program perlu menggunakan fungsi serupa untuk meneruskan tiga nilai bilangan bulat, maka fungsi dengan nama yang berbeda harus dibuat. Misalnya, tambah_dua_nilai dan tambah_tiga_nilai. Demikian pula, jika Anda ingin menggunakan fungsi serupa untuk bekerja dengan nilai float, Anda memerlukan fungsi lain dengan nama lain. Untuk menghindari duplikasi fungsi, C++ memungkinkan Anda untuk mendefinisikan beberapa fungsi dengan nama yang sama. Selama kompilasi, C++ memperhitungkan jumlah argumen yang digunakan oleh setiap fungsi dan kemudian memanggil fungsi yang diperlukan dengan tepat. Memberi kompiler pilihan di antara banyak fungsi disebut kelebihan beban.

Kelebihan fungsi adalah pembuatan beberapa fungsi dengan nama yang sama, tetapi dengan parameter yang berbeda. Parameter yang berbeda berarti apa yang harus berbeda sejumlah argumen fungsi dan/atau fungsinya jenis. Yaitu, kelebihan beban fungsi memungkinkan Anda untuk menentukan beberapa fungsi dengan nama dan tipe kembalian yang sama.

Overloading fungsi juga disebut polimorfisme fungsi. "Poli" artinya banyak, "morf" adalah bentuk, yaitu fungsi polimorfik adalah fungsi yang dibedakan dengan berbagai bentuk.

Polimorfisme fungsi dipahami sebagai keberadaan dalam program beberapa versi kelebihan beban dari suatu fungsi yang memiliki nilai berbeda. Dengan mengubah nomor atau jenis parameter, Anda dapat memberi dua atau lebih fungsi nama yang sama. Dalam hal ini, tidak akan ada kebingungan, karena fungsi yang diinginkan ditentukan oleh kebetulan dari parameter yang digunakan. Ini memungkinkan Anda membuat fungsi yang dapat bekerja dengan bilangan bulat, float, atau jenis nilai lainnya tanpa harus membuat nama terpisah untuk setiap fungsi.

Jadi, berkat penggunaannya fungsi kelebihan beban, Anda tidak perlu khawatir tentang pemanggilan fungsi yang tepat dalam program yang cocok dengan tipe variabel yang diteruskan. Saat Anda memanggil fungsi yang kelebihan muatan, kompiler akan secara otomatis menentukan versi fungsi mana yang harus digunakan.

Misalnya, program berikut membebani fungsi bernama add_values ​​. Definisi fungsi pertama menambahkan dua nilai bertipe int . Definisi fungsi kedua menambahkan tiga nilai bertipe int . Selama kompilasi, C++ dengan benar menentukan fungsi yang akan digunakan:

#include "stdafx.h" #include menggunakan namespace std; int nilai_tambah(int a,int b); int nilai_tambah (int a, int b, int c); int _tmain(int argc, _TCHAR* argv)( cout<< "200+801=" << add_values(200,801) << "\n"; cout << "100+201+700=" << add_values(100,201,700) << "\n"; system("pause"); return 0; } int add_values(int a,int b) { return(a + b); } int add_values (int a, int b, int c) { return(a + b + c); }

Jadi, program mendefinisikan dua fungsi bernama add_values ​​. Fungsi pertama menambahkan dua nilai, sedangkan yang kedua menambahkan tiga nilai dengan tipe int yang sama. Kompiler C++ menentukan fungsi mana yang akan digunakan berdasarkan opsi yang disediakan oleh program.

Menggunakan fungsi yang berlebihan

Salah satu kasus penggunaan yang paling umum untuk kelebihan muatan adalah menggunakan fungsi untuk menghasilkan hasil tertentu dengan berbagai parameter. Misalnya, program Anda memiliki fungsi bernama day_of_week yang mengembalikan hari saat ini dalam seminggu (0 untuk Minggu, 1 untuk Senin, ... , 6 untuk Sabtu). Suatu program dapat membebani fungsi ini sehingga mengembalikan hari dalam seminggu dengan benar jika diberikan hari Julian sebagai parameter, atau jika diberikan hari, bulan, dan tahun.

int hari_minggu(int julian_hari) ( // operator ) int hari_minggu(int bulan, int hari, int tahun) ( // operator )

Menggunakan fungsi kelebihan beban sejumlah kesalahan sering dilakukan. Misalnya, jika fungsi hanya berbeda dalam tipe pengembalian, tetapi tidak dalam tipe argumen, fungsi tersebut tidak dapat memiliki nama yang sama. Opsi kelebihan beban berikut ini juga tidak valid:

int nama_fungsi(int nama_argumen); int nama_fungsi(int nama_argumen); /* kelebihan nama tidak valid: argumen memiliki nomor dan tipe yang sama */

Manfaat kelebihan fungsi:

  • fungsi overloading meningkat keterbacaan program;
  • Kelebihan fungsi C++ memungkinkan program untuk mendefinisikan beberapa fungsi dengan nama yang sama;
  • fungsi kelebihan beban mengembalikan nilai dengan jenis yang sama, tetapi mungkin berbeda dalam jumlah dan jenis parameter;
  • kelebihan fungsi menyederhanakan tugas pemrogram dengan mengharuskan mereka untuk mengingat hanya satu nama fungsi, tetapi kemudian mereka harus mengetahui kombinasi parameter mana yang sesuai dengan fungsi mana.

Contoh 2.

/*Fungsi kelebihan muatan memiliki nama yang sama tetapi daftar parameter dan nilai kembalian berbeda*/ #include "stdafx.h" #include menggunakan namespace std; int rata-rata(int bilangan_pertama, int bilangan_kedua, int bilangan_ketiga); int rata-rata(int first_number, int second_number); int _tmain(int argc, _TCHAR* argv)(// fungsi utama int angka_A = 5, angka_B = 3, angka_C = 10; cout<< "Целочисленное среднее чисел " << number_A << " и "; cout << number_B << " равно "; cout << average(number_A, number_B) << ".\n\n"; cout << "Целочисленное среднее чисел " << number_A << ", "; cout << number_B << " и " << number_C << " равно "; cout << average(number_A, number_B, number_C) << ".\n"; system("PAUSE"); return 0; }// конец главной функции /*функция для вычисления целочисленного среднего значения 3-х целых чисел*/ int average(int first_number, int second_number, int third_number) { return((first_number + second_number + third_number)/3); } // конец функции /*функция для вычисления целочисленного среднего значения 2-х целых чисел*/ int average(int first_number, int second_number) { return((first_number + second_number)/2); } // конец функции



Memuat...
Atas