Saat kita memanggil fungsi dengan argumen, apa sebenarnya yang dikirim ke dalamnya — nilai aslinya, atau salinannya? Jawaban default C++ adalah: salinan. Mekanisme ini disebut pass by value, dan konsekuensinya fundamental: fungsi bekerja pada duplikat data, sehingga apa pun yang dilakukannya pada parameter tidak menyentuh variabel asli di pemanggil. Pada Chapter ini kita membuktikan perilaku itu dengan eksperimen nyata, memahami mengapa keamanan semacam ini justru diinginkan, lalu menakar biayanya untuk data berukuran besar.

Yang Dikirim Adalah Salinan #

Berikut program dengan dua fungsi yang tampak “mengubah” datanya: satu mengalikan angka, satu menambahkan teks. Keduanya menerima parameter secara pass-by-value — tanpa tanda tambahan apa pun:

cpp
#include <iostream>
#include <string>

void ubahAngka(int x) {
    x = x * 100;   // hanya salinan yang berubah
    std::cout << "Di dalam ubahAngka : " << x << "\n";
}

void ubahTeks(std::string teks) {
    teks = teks + " (sudah diedit)";
    std::cout << "Di dalam ubahTeks  : " << teks << "\n";
}

int main() {
    int angka = 7;
    std::string teks = "pesan";

    ubahAngka(angka);
    ubahTeks(teks);

    std::cout << "Setelah pemanggilan: angka = " << angka << "\n";
    std::cout << "Setelah pemanggilan: teks  = " << teks << "\n";
    return 0;
}

Output yang Dihasilkan:

text
Di dalam ubahAngka : 700
Di dalam ubahTeks  : pesan (sudah diedit)
Setelah pemanggilan: angka = 7
Setelah pemanggilan: teks  = pesan

Mekanismenya begini: setiap pemanggilan membuat parameter sebagai variabel lokal baru yang DIINISIALISASI dari argumen — persis seperti int x = angka; di dalam kepala fungsi. Di dalam ubahAngka, yang berubah menjadi 700 adalah salinan x; variabel angka milik main() tidak tersentuh sama sekali, dan buktinya terlihat pada dua baris terakhir output. Perhatikan juga bahwa operasi teks + " (sudah diedit)" berdampak hanya pada salinan teks; string asli tetap "pesan". Parameter, seperti variabel lokal lain, berakhir di akhir fungsi — siklus hidupnya mengikuti aturan scope yang kita bahas pada Section Control Flow.

Mengapa Perilaku Ini Justru Berguna #

Pass by value menjadikan fungsi terisolasi dari sekelilingnya. Pemanggil bisa yakin bahwa memanggil hitungTotal(harga, jumlah) tidak mengubah harga atau jumlah secara diam-diam, sekecil apa pun logika di dalamnya. Isolasi ini membuat kode mudah dinalar: efek sebuah fungsi terbatas pada return value dan tugas yang memang dinyatakannya. Dalam istilah populer, pass by value adalah default yang aman — bug akibat perubahan data yang tidak disengaja tertahan di gerbang parameter.

Keterbatasannya pun jelas: fungsi pass-by-value tidak bisa mengembalikan hasil lewat parameternya. Bila tugas kita memang membutuhkan perubahan pada data pemanggil — misalnya menukar dua variabel — pass by value tidak mampu; alatnya adalah pass by reference pada Chapter berikutnya.

Biaya Salinan untuk Data Besar #

Kemudahan pass by value ada harganya: setiap pemanggilan menyalin seluruh argumen. Untuk int, double, char, atau bool — tipe-tipe berukuran beberapa byte — salinan itu nyaris tanpa biaya dan inilah cara passing yang paling wajar. Ceritanya berbeda untuk data besar: std::string berisi 100.000 karakter yang dikirim ke fungsi akan disalin karakter demi karakter, 100.000 kali per pemanggilan, setiap kali fungsi itu dipanggil dalam loop. Waktu dan memori terbuang untuk menyalin data yang sebenarnya hanya ingin dibaca.

Catatan optimasi: compiler modern boleh menghilangkan sebagian salinan lewat mekanisme copy elision dalam kondisi tertentu, tetapi kita tidak boleh mengandalkannya sebagai jaminan. Rujukan resmi tentang parameter fungsi ada di cppreference: functions , dan tentang penghapusan salinan di cppreference: copy elision . Lantas bagaimana caranya MEMBUAT fungsi yang memang ingin mengubah data pemanggil? Ikuti ke Chapter berikutnya.