Menu

Type casting C++: static_cast, konversi implisit, dan cast

Cara kerja type casting di C++: konversi implisit, jebakan pembagian integer, dan empat cast bernama (static_cast, const_cast, reinterpret_cast, dynamic_cast), plus jebakan yang menyebabkan data hilang diam-diam.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Apa itu type casting

Type casting berarti mengubah nilai dari satu tipe ke tipe lain: mengubah double menjadi int, char menjadi kode angkanya, atau pointer class induk menjadi pointer class turunan. C++ melakukan sebagian konversi ini secara otomatis untukmu, tapi konversi yang dilakukan diam-diam itulah tempat bug bersembunyi.

Ada dua jenis: konversi implisit yang terjadi dengan sendirinya, dan cast eksplisit yang kamu tulis sendiri. Kamu sudah bertemu satu gejalanya di halaman operator, yaitu pembagian integer. Casting adalah cara kamu mengendalikannya.

Konversi implisit

Saat kamu mencampur tipe angka dalam sebuah ekspresi, C++ mempromosikan tipe yang "lebih kecil" ke tipe yang "lebih besar" supaya kedua sisinya cocok. Biasanya hasilnya sesuai keinginanmu.

Masalahnya mulai saat konversi berjalan ke arah sebaliknya, dari tipe yang lebih lebar ke yang lebih sempit. Itu disebut konversi narrowing, dan bisa menghilangkan data diam-diam.

Float ke int memotong ke arah nol, bukan membulatkan, jadi 3.99 menjadi 3. Dan memasukkan 300 ke char menyebabkan overflow. Banyak compiler memberi peringatan di sini; sebagian tidak. Kalau kamu memang bermaksud melakukan narrowing, tulis secara eksplisit dengan cast supaya pembaca berikutnya tahu itu disengaja.

Memperbaiki jebakan pembagian integer

Alasan paling umum untuk melakukan cast adalah pembagian. Kalau kedua operand-nya integer, / melakukan pembagian integer dan membuang sisanya.

Solusinya adalah static_cast<double> pada salah satu operand sebelum pembagian. Kesalahan yang sering terjadi adalah static_cast<double>(got / total): itu terlambat, karena got / total sudah bernilai 0 saat cast dijalankan, sehingga kamu mendapat 0.0. Lakukan cast pada operand, bukan pada hasilnya.

static_cast: cast default-mu

C++ memberimu empat cast bernama. Yang akan kamu pakai 95% dari waktu adalah static_cast<T>(value), yang melakukan konversi yang terdefinisi dengan baik antara tipe-tipe yang berkaitan: konversi angka, enum ke int, void* kembali ke pointer bertipe, dan naik/turun hierarki class saat kamu sudah tahu tipenya.

Pilih static_cast daripada cast gaya C yang lama, (int)balance. Cast gaya C akan mencoba konversi apa saja supaya kodenya bisa dikompilasi, termasuk yang berbahaya di bawah ini, jadi ia bisa diam-diam menghapus const atau menafsirkan ulang byte mentah. static_cast hanya mengizinkan konversi yang benar-benar bisa dibenarkan compiler, dan tulisan static_cast<...> yang panjang itu sangat mudah dicari saat code review.

// Avoid - C-style cast, no safety net:
int dollars = (int) balance;

// Prefer - explicit, checked, greppable:
int dollars = static_cast<int>(balance);

Tiga cast lainnya (pakai seperlunya)

Cast lainnya ada untuk tugas-tugas khusus yang sempit. Pakai hanya saat static_cast memang tidak bisa melakukannya.

const_cast menghapus (atau menambahkan) const. Satu-satunya pemakaian yang sah adalah memanggil API gaya C yang lupa menandai parameternya const. Mengubah objek yang awalnya dideklarasikan const lewat const_cast adalah undefined behavior.

void legacyApi(char* msg);   // old API, doesn't take const

const char* text = "hello";
legacyApi(const_cast<char*>(text));   // only OK if legacyApi doesn't write to it

reinterpret_cast menafsirkan ulang pola bit mentah, misalnya pointer sebagai alamat integer. Ia sama sekali tidak melakukan konversi dan sangat tidak aman; hampir selalu ini tanda bahwa kamu perlu memikirkan ulang desainnya.

dynamic_cast dengan aman mengonversi pointer atau reference class induk turun ke tipe turunan saat runtime, berdasarkan tipe objek yang sebenarnya. Ia membutuhkan class induk yang polimorfik (class dengan minimal satu virtual function) dan mengembalikan nullptr kalau cast-nya tidak berlaku.

Kalau a menunjuk ke Animal lain, dynamic_cast<Dog*> akan mengembalikan nullptr dan cabang else yang dijalankan. Itulah alasan ia lebih aman daripada asal memakai static_cast untuk turun di hierarki class.

Kesalahan umum yang perlu dihindari

  • Melakukan cast pada hasil, bukan pada operand. static_cast<double>(a / b) membuang pecahanmu lebih dulu. Lakukan cast pada a atau b.
  • Mengira float ke int itu membulatkan. Yang terjadi adalah pemotongan: static_cast<int>(2.99) hasilnya 2. Untuk pembulatan pakai std::round, std::lround, dan sejenisnya.
  • Memakai cast gaya C. Cast ini menyembunyikan konversi mana yang terjadi. Pakai static_cast, maka kamu akan mendapat error kompilasi saat konversinya tidak aman, bukan kejutan diam-diam.
  • Narrowing ke tipe yang terlalu kecil. Cast 300 ke char atau long yang sangat besar ke int akan berputar atau overflow. Pilih tipe tujuan yang cukup lebar untuk rentang nilainya.

Berikutnya: if-else

Sekarang kamu sudah bisa mengonversi dan membandingkan nilai dengan rapi, langkah berikutnya adalah membuat keputusan dengannya. Pernyataan if-else menjalankan kode yang berbeda tergantung apakah sebuah kondisi bernilai true, fondasi dari setiap program yang punya percabangan.

Pertanyaan yang sering diajukan

Apa perbedaan static_cast dan cast gaya C di C++?

Cast gaya C seperti (int)x mencoba setiap jenis konversi satu per satu, jadi ia bisa diam-diam menjadi reinterpret_cast yang berbahaya atau menghapus const. static_cast<int>(x) hanya melakukan konversi yang berkaitan dan bisa diverifikasi compiler, sehingga compiler menolak konversi yang tidak masuk akal. Di C++ modern, selalu pilih static_cast daripada cast gaya C: lebih aman dan jauh lebih mudah dicari dengan grep.

Bagaimana cara melakukan cast int ke double di C++?

Pakai static_cast<double>(x). Ini paling penting saat pembagian: 5 / 2 adalah pembagian integer dan hasilnya 2, tapi static_cast<double>(5) / 2 hasilnya 2.5. Lakukan cast pada salah satu operand sebelum pembagian terjadi. Melakukan cast pada hasilnya, static_cast<double>(5 / 2), sudah terlambat dan tetap menghasilkan 2.0.

Kenapa cast nilai besar ke tipe yang lebih kecil menghasilkan angka yang salah di C++?

Mengonversi ke tipe yang tidak muat untuk nilainya adalah konversi narrowing. Float ke int memotong bagian pecahan (static_cast<int>(3.99) hasilnya 3), dan integer di luar rentang akan berputar (unsigned) atau bergantung pada implementasi (signed). Compiler biasanya tidak akan menghentikanmu, jadi lakukan cast dengan sengaja dan pastikan tipe tujuannya cukup lebar.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai