Kata kunci const dapat diletakkan pada tiga posisi yang berbeda dalam deklarasi pointer, dan setiap posisi mengunci bagian yang berbeda. Kombinasi const T*, T* const, dan const T* const menentukan apakah nilai yang ditunjuk boleh ditulis lewat pointer, apakah tujuan pointer boleh diganti, atau keduanya. Topik ini membahas ketiga bentuk tersebut, cara membacanya dari kanan ke kiri, dan pemakaiannya pada parameter fungsi.
Tiga bentuk const pada pointer#
Bentuk const T* menyorot nilai yang ditunjuk. Pointer boleh berpindah ke objek lain, tetapi nilai pada objek yang sedang ditunjuk tidak boleh ditulis melaluinya. Bentuk ini paling sering muncul karena cocok untuk membaca data besar tanpa menyalinnya, dan bentuk yang setara T const* memiliki arti yang persis sama.
Bentuk T* const menyorot pointer itu sendiri. Tujuannya terkunci sejak inisialisasi dan wajib diberi nilai saat dideklarasikan, sedangkan nilai pada objek yang ditunjuk tetap boleh ditulis. Bentuk ketiga, const T* const, mengunci keduanya, sehingga pointer tidak dapat berpindah dan nilai pada objek yang ditunjuk juga tidak dapat ditulis. Program berikut menjalankan satu operasi yang sah untuk setiap bentuk.
#include <iostream>
int main() {
int a = 10;
int b = 20;
const int* pNilai = &a; // nilai yang ditunjuk tidak boleh ditulis lewat p
int* const pTujuan = &a; // tujuan pointer tidak boleh diganti
const int* const pKeduanya = &a; // keduanya terkunci
pNilai = &b;
std::cout << "*pNilai = " << *pNilai << '\n';
std::cout << "*pKeduanya = " << *pKeduanya << '\n';
*pTujuan = 30;
std::cout << "a = " << a << '\n';
return 0;
}*pNilai = 20
*pKeduanya = 10
a = 30pNilai berpindah ke b tanpa masalah karena bentuk pertama hanya mengunci nilainya, sedangkan *pTujuan = 30 sah karena bentuk kedua hanya mengunci tujuan pointer. Nilai yang dicetak melalui *pKeduanya masih 10 karena baris pencetakannya dijalankan sebelum *pTujuan = 30, dan pKeduanya menunjuk a yang belum berubah pada saat itu. Adapun baris yang melanggar kunci justru ditolak compiler sebelum program dijalankan, sebagaimana ditunjukkan contoh berikut.
Bentuk pointer ke const juga boleh menunjuk objek yang benar-benar konst. Deklarasi const int tetap = 5; diikuti const int* p = &tetap; sah sepenuhnya, sedangkan pointer non-const int* q = &tetap; ditolak compiler. Aturan ini menjaga objek konst tetap terlindungi dari setiap jalur penulisan.
// tidak-dijalankan (kedua baris terakhir menghasilkan error kompilasi)
int a = 10;
const int* pNilai = &a;
int* const pTujuan = &a;
*pNilai = 5; // error: nilai tidak boleh ditulis lewat const int*
pTujuan = nullptr; // error: tujuan T* const tidak boleh diganti
Cara membaca deklarasi dari kanan ke kiri#
Deklarasi pointer dengan const paling mudah dibaca mulai dari nama variabelnya, bergerak ke kanan, lalu ke kiri. Deklarasi const int* p dibaca “p adalah pointer ke int yang konst”, sedangkan int* const p dibaca “p adalah pointer konst ke int”. Urutan kata inilah yang menentukan bagian yang terkunci, dan tabel berikut merangkum pembacaan ketiga bentuknya.
| Deklarasi | Pointer berpindah | Nilai ditulis lewat pointer | Cara membaca |
|---|---|---|---|
const T* p | Boleh | Tidak | pointer ke T konst |
T* const p | Tidak | Boleh | pointer konst ke T |
const T* const p | Tidak | Tidak | pointer konst ke T konst |
Aturan posisinya sederhana. Kata const yang berada di sebelah kiri tanda * mengunci nilai yang ditunjuk, sedangkan kata const di sebelah kanan tanda * mengunci pointer itu sendiri. Aturan ini membuat deklarasi yang lebih panjang, misalnya const char* const p, tetap dapat dibaca tanpa menghafal, karena setiap kata const dievaluasi menurut posisinya terhadap tanda *.
Bentuk penulisan T const* menempatkan kata const setelah tipe, dan artinya identik dengan const T*. Beberapa pustaka dan gaya penulisan memilih bentuk ini karena urutannya mengikuti cara membaca dari kanan ke kiri. Keduanya sah, sehingga pilihan cukup dijaga konsisten di seluruh proyek.
const pada parameter fungsi#
Parameter const T* adalah bentuk yang paling lazim untuk fungsi yang hanya membaca data. Jaminan hanya-baca itu tercatat pada tanda tangan fungsi, yaitu baris deklarasi yang memuat nama dan parameter fungsi, sehingga terbaca tanpa membuka badan fungsinya. Compiler menolak setiap penulisan lewat parameter tersebut, sehingga pelanggarannya tertangkap saat kompilasi, bukan saat program berjalan.
Kebiasaan ini sejajar dengan penggunaan const T& pada parameter reference yang dibahas pada modul fungsi. Pembedaannya sederhana. const T* digunakan ketika ketiadaan objek juga perlu dinyatakan secara sah, misalnya melalui nullptr, sedangkan const T& digunakan ketika objek pasti ada. Keduanya sama-sama menghindari penyalinan dan menjamin data tidak diubah.
Jaminan pada parameter berlaku dua arah. Di satu sisi, compiler menolak penulisan lewat parameter konst di dalam fungsi. Di sisi lain, fungsi yang menerima const T* tetap dapat menerima alamat objek non-const maupun konst. Karena itu parameter konst tidak membatasi pemanggil, melainkan memperluas rentang nilai yang dapat diterima, sembari menjamin data tidak berubah setelah fungsi berakhir.
Bentuk T* const jarang dipakai sebagai parameter karena parameternya hanyalah salinan lokal. Mengunci salinan tersebut tidak memberi jaminan apa pun kepada pemanggil, sehingga bentuk ini lebih sering muncul sebagai variabel lokal yang tujuannya memang tidak berubah, misalnya pointer yang mengunci dirinya ke satu buffer sepanjang fungsinya.
Kesalahan umum#
Pembacaan cepat yang hanya melihat adanya kata const tanpa posisinya sering keliru. Deklarasi int const* p dan const int* p sama saja, tetapi int* const p berbeda total, sehingga satu perpindahan kata mengubah seluruh arti deklarasi. Periksa letak setiap kata const terhadap tanda * setiap kali deklarasi semacam ini dibaca atau ditulis, dan tabel pada bagian sebelumnya menjadi acuan yang cepat.
Kesalahan lain adalah menganggap const T* menjadikan objek yang ditunjuk menjadi permanen. Objeknya tidak menjadi konst, melainkan hanya terlindungi dari penulisan lewat jalur itu. Apabila objek yang sama masih dapat dijangkau lewat pointer atau referensi non-const, nilainya tetap boleh diubah dari jalur tersebut, dan pointer konst akan membaca nilai yang sudah berubah. Pemahaman ini penting agar perubahan yang terbaca melalui pointer konst tidak menimbulkan kebingungan.
Ringkasan#
const T*mengunci nilai yang ditunjuk,T* constmengunci tujuan pointer, danconst T* constmengunci keduanya.- Deklarasi dibaca dari nama variabel ke kanan lalu ke kiri, dan posisi kata
constterhadap tanda*menentukan bagian yang terkunci. - Parameter
const T*menyatakan komitmen hanya membaca, sehingga compiler menolak setiap penulisan lewat jalur itu. const T*tidak menjadikan objeknya konst, sehingga nilai yang sama tetap boleh diubah lewat jalur non-const lain.
Referensi#
- const/volatile qualifications — cppreference.com — Kaidah kualifikasi const pada tipe dan pointer.
- Pointers and const — LearnCpp — Bedah pointer ke const dan pointer const beserta contohnya.
- Pointer declaration — cppreference.com — Deklarasi pointer beserta kualifikasi const-nya.