Fungsi adalah unit terkecil untuk mengorganisir logika algoritma. Dengan fungsi, kita memecah masalah besar menjadi langkah-langkah kecil yang dapat diuji secara terpisah, diberi nama yang menjelaskan maksudnya, dan dipakai ulang. Pada Chapter ini kita akan mempelajari cara mendefinisikan fungsi, cara melewatkan parameter (by value, by reference, by const reference), cara membebani nama fungsi, serta pengantar rekursi yang menjadi pondasi banyak algoritma DSA.

Definisi Fungsi #

Sebuah fungsi C++ terdiri dari return type, nama, daftar parameter dalam tanda kurung, dan badan fungsi dalam {}. Compiler harus melihat deklarasi fungsi sebelum digunakan, sehingga biasanya fungsi didefinisikan di atas main atau dideklarasikan dulu sebagai prototype di header.

Mekanisme: Parameter dan Return Value #

Saat fungsi dipanggil, argumen disalin ke parameter lokal fungsi (pass by value). Saat fungsi menjalankan return, nilai dikirim kembali ke pemanggil dan parameter lokal dihancurkan.

Kode: Fungsi Penjumlahan Sederhana #

cpp
#include <iostream>

int tambah(int a, int b) {
    return a + b;
}

int main() {
    int x = 10, y = 5;
    std::cout << "10 + 5 = " << tambah(x, y) << '\n';
    return 0;
}

Output yang Dihasilkan:

text
10 + 5 = 15
  1. Baris int tambah(int a, int b): return type int, dua parameter int. Nama fungsi tambah.
  2. Baris return a + b;: hasil ekspresi dikirim ke pemanggil. Setelah return, parameter a dan b dihancurkan.
  3. Baris tambah(x, y): nilai x disalin ke a, y disalin ke b. Variabel x dan y di main tidak terpengaruh.

Pass by Value vs Reference vs Const Reference #

Cara melewatkan parameter menentukan apakah fungsi bisa memodifikasi variabel pemanggil dan seberapa mahal operasi copy-nya. Tiga varian utama:

VarianSintaksModifikasi?Copy mahal?
By valueint aTidak (copy)Ya
By referenceint& aYaTidak
By const referenceconst int& aTidakTidak

Kode: Perbandingan Tiga Varian #

cpp
#include <iostream>
#include <vector>

int tambah_salin(int a, int b) {     // pass by value
    a = a + b;
    return a;
}

void tambah_ref(int& a, int b) {       // pass by reference
    a = a + b;
}

long long sum(const std::vector<int>& v) {  // const reference
    long long total = 0;
    for (int x : v) total += x;
    return total;
}

int main() {
    int x = 10;
    int hasil_val = tambah_salin(x, 5);
    std::cout << "setelah salin: x=" << x << " hasil=" << hasil_val << '\n';

    tambah_ref(x, 5);
    std::cout << "setelah ref:   x=" << x << '\n';

    std::vector<int> data{1, 2, 3, 4, 5};
    std::cout << "sum            = " << sum(data) << '\n';
    return 0;
}

Output yang Dihasilkan:

text
setelah salin: x=10 hasil=15
setelah ref:   x=15
sum            = 15
  1. Fungsi tambah_salin(int a, int b): a adalah copy dari x. Memodifikasi a tidak memengaruhi x di main. Setelah pemanggilan, x tetap 10.
  2. Fungsi tambah_ref(int& a, int b): a adalah alias dari x. Memodifikasi a sama dengan memodifikasi x langsung. Setelah pemanggilan, x menjadi 15.
  3. Fungsi sum(const std::vector<int>& v): v adalah referensi const — tidak ada copy vector (yang mahal), tetapi fungsi tidak bisa memodifikasi elemen v.

Default Argument #

C++ mengizinkan parameter memiliki nilai default bila tidak disediakan pemanggil. Default harus berada di posisi paling kanan daftar parameter.

Kode: Fungsi Pangkat dengan Default #

cpp
#include <iostream>

int pangkat(int base, int exp = 2) {
    int hasil = 1;
    for (int i = 0; i < exp; ++i) hasil *= base;
    return hasil;
}

int main() {
    std::cout << "pangkat(3)   = " << pangkat(3) << '\n';      // 3^2
    std::cout << "pangkat(3,4) = " << pangkat(3, 4) << '\n';   // 3^4
    return 0;
}

Output yang Dihasilkan:

text
pangkat(3)   = 9
pangkat(3,4) = 81
  1. Baris int exp = 2: parameter exp memiliki default 2. Pemanggil boleh tidak menyediakan argumen kedua.
  2. Baris pangkat(3): hanya satu argumen; exp otomatis 2, menghitung 3^2 = 9.
  3. Baris pangkat(3, 4): dua argumen disediakan; default ditimpa, menghitung 3^4 = 81.

Function Overloading #

C++ mengizinkan beberapa fungsi berbagi nama asalkan parameter berbeda (jumlah atau tipe). Compiler memilih versi yang tepat berdasarkan tipe argumen saat pemanggilan.

Kode: Overload maks untuk int dan double #

cpp
#include <iostream>

int maks(int a, int b) {
    return (a > b) ? a : b;
}

double maks(double a, double b) {
    return (a > b) ? a : b;
}

int main() {
    std::cout << "maks(int)    = " << maks(7, 12) << '\n';
    std::cout << "maks(double) = " << maks(3.5, 2.1) << '\n';
    return 0;
}

Output yang Dihasilkan:

text
maks(int)    = 12
maks(double) = 3.5
  1. Baris maks(7, 12): kedua argumen bertipe int, compiler memilih overload int maks(int, int).
  2. Baris maks(3.5, 2.1): argumen bertipe double, compiler memilih overload double maks(double, double).
  3. Overload tidak boleh hanya berbeda di return type — daftar parameter harus berbeda.

Rekursi Dasar #

Rekursi adalah teknik di mana fungsi memanggil dirinya sendiri untuk menyelesaikan sub-masalah yang lebih kecil. Banyak algoritma DSA — tree traversal, merge sort, backtracking — secara natural diekspresikan secara rekursif.

Mekanisme: Base Case dan Recursive Case #

Setiap fungsi rekursif wajib memiliki base case (kasus dasar yang berhenti) dan recursive case (panggilan diri sendiri dengan input lebih kecil). Tanpa base case, rekursi berjalan selamanya hingga stack overflow.

Kode: Factorial Rekursif #

cpp
#include <iostream>

long long factorial(int n) {
    if (n <= 1) return 1;        // base case
    return n * factorial(n - 1);  // recursive case
}

int main() {
    std::cout << "factorial(5) = " << factorial(5) << '\n';
    return 0;
}

Output yang Dihasilkan:

text
factorial(5) = 120
  1. Baris if (n <= 1) return 1;: base case. Saat n mencapai 1, rekursi berhenti dan mengembalikan 1. Tanpa baris ini, fungsi akan memanggil dirinya hingga n sangat negatif dan stack overflow.
  2. Baris return n * factorial(n - 1);: recursive case. Sub-masalah lebih kecil (n-1) diselesaikan dulu, lalu dikalikan n.
  3. Untuk n = 5, urutan pemanggilan: 5 * factorial(4) = 5 * 4 * factorial(3) = ... = 5 * 4 * 3 * 2 * 1 = 120.

Ringkasan #

  • Fungsi memecah algoritma menjadi unit yang dapat diuji dan dipakai ulang.
  • Pass by value aman tapi copy mahal; by reference untuk modifikasi; by const reference untuk efisiensi read-only.
  • Default argument mengurangi duplikasi overload; tempatkan default di kanan.
  • Overload memungkinkan satu nama dengan beberapa perilaku berdasarkan tipe argumen.
  • Rekursi butuh base case yang jelas; waspadai stack overflow pada kedalaman besar.