Selama ini fungsi kita panggil dengan namanya, dan hubungannya tertulis permanen di source code. C++ menyediakan tingkat kebebasan lain: menyimpan alamat fungsi ke dalam variabel — itulah function pointer, pointer yang menunjuk kode, bukan data. Dengan itu, pemilihan fungsi bisa ditentukan SAAT PROGRAM BERJALAN: satu baris pemanggilan, perilaku yang bisa berganti. Sebagai konteks singkat: pointer adalah variabel yang menyimpan alamat memori, topik yang akan kita dalami tuntas pada Section Pointer dan Reference; di sini kita cukup memakai bentuk khususnya untuk fungsi.

Tipe Function Pointer #

Setiap fungsi punya tipe yang ditentukan return type dan daftar parameternya — tambah bertipe “fungsi yang menerima dua int dan mengembalikan int”. Function pointer menuliskan tipenya dengan tanda kurung di sekitar *nama:

cpp
#include <iostream>

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

int main() {
    // Deklarasi + assignment: operasi menunjuk ke fungsi tambah
    int (*operasi)(int, int) = tambah;

    std::cout << "operasi(3, 4)   = " << operasi(3, 4) << "\n";

    // Mengganti target: cukup assignment ulang
    operasi = kali;
    std::cout << "operasi(3, 4)   = " << operasi(3, 4) << "\n";

    // Pemanggilan lewat dereference eksplisit juga sah
    std::cout << "(*operasi)(5, 6) = " << (*operasi)(5, 6) << "\n";
    return 0;
}

Output yang Dihasilkan:

text
operasi(3, 4)   = 7
operasi(3, 4)   = 12
(*operasi)(5, 6) = 30

Baris kuncinya adalah int (*operasi)(int, int) = tambah; — dibaca: “operasi adalah pointer ke fungsi yang menerima dua int dan mengembalikan int, isinya alamat fungsi tambah”. Kurung di sekitar *operasi WAJIB; tanpa itu, int *operasi(int, int) justru dibaca sebagai deklarasi fungsi yang mengembalikan int*. Nama fungsi tanpa kurung — tambah — otomatis meluruh (decay) menjadi alamatnya, jadi tidak perlu menulis &tambah, meski bentuk itu pun sah. Pemanggilan bisa lewat operasi(3, 4) langsung atau dereference eksplisit (*operasi)(5, 6); keduanya setara, dan yang pertama lebih lazim. Terakhir, perhatikan bahwa dua baris operasi(3, 4) yang sama persis menghasilkan dua jawaban berbeda — 7 lalu 12 — karena target pointer diganti di antaranya. Itulah kekuatan utama fitur ini: keputusan yang dulu terkunci saat kompilasi kini bisa berganti saat program berjalan.

Callback: Fungsi sebagai Parameter #

Function pointer paling bermanfaat sebagai parameter — pola bernama callback: fungsi penerima menjalankan tugas kerangkanya, sementara langkah yang boleh diganti diserahkan kepada pemanggil. Deklarasinya panjang dan sulit dibaca, jadi kita rapikan dengan alias tipe using yang kita pelajari pada Chapter Using dan Type Alias:

cpp
#include <iostream>
#include <string>
#include <vector>

// Alias tipe membuat deklarasi function pointer jauh lebih terbaca
using Transformasi = int (*)(int);

int kuadrat(int x) { return x * x; }
int negasi(int x) { return -x; }

// Callback: fungsi menerima fungsi lain sebagai parameter
void terapkan(const std::vector<int>& data, Transformasi f, std::string label) {
    std::cout << label;
    bool pertama = true;
    for (int nilai : data) {
        if (!pertama) {
            std::cout << " ";
        }
        pertama = false;
        std::cout << f(nilai);
    }
    std::cout << "\n";
}

int main() {
    std::vector<int> angka = {1, 2, 3, 4};
    terapkan(angka, kuadrat, "Kuadrat: ");
    terapkan(angka, negasi, "Negasi : ");
    return 0;
}

Output yang Dihasilkan:

text
Kuadrat: 1 4 9 16
Negasi : -1 -2 -3 -4

using Transformasi = int (*)(int); menciptakan nama tipe yang jauh lebih manusiawi daripada sintaks mentahnya, dan parameter Transformasi f terbaca seperti parameter biasa. Perhatikan pembagian perannya: terapkan TIDAK tahu apa-apa tentang kuadrat atau negasi — ia hanya menjalankan f(nilai) untuk setiap elemen std::vector<int> (container dinamis yang kita kenal di Section Control Flow; parameter data-nya memakai const& seperti pada Chapter Pass by Const-Reference). Pemanggil yang menentukan langkahnya: kuadrat untuk panggilan pertama, negasi untuk panggilan kedua. Cara kerja qsort di pustaka C dan banyak API lama persis seperti ini.

Kapan Function Pointer Layak Dipakai #

Function pointer tetap relevan di tiga wilayah utama:

  • API yang menerima perilaku dari luar: callback pembanding untuk pengurutan, handler peristiwa, fungsi pembersih yang dipanggil library saat selesai.
  • Tabel perintah: array of function pointer memetakan pilihan menu atau kode operasi ke fungsinya tanpa rantai switch yang panjang.
  • Antarmuka dengan pustaka C: pustaka C tidak mengenal lambda C++, jadi function pointer adalah mata uang yang wajib di sana.

Rujukan resmi: cppreference: pointers to functions dan cppreference: type alias . Membuat fungsi bernama hanya untuk dipakai sebagai callback terasa berat — Chapter berikutnya memperkenalkan cara menulis fungsi tanpa nama, langsung di tempat pemakaiannya.