Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Konversi menghasilkan nilai baru dari beberapa jenis dari nilai dengan jenis yang berbeda. Konversi standar dibangun ke dalam bahasa C++ dan mendukung jenis bawaannya, dan Anda dapat membuat konversi yang ditentukan pengguna untuk melakukan konversi ke, dari, atau di antara jenis yang ditentukan pengguna.
Konversi standar melakukan konversi antara jenis bawaan, antara penunjuk atau referensi ke jenis yang terkait dengan pewarisan, ke dan dari pointer yang batal, dan ke penunjuk null. Untuk informasi selengkapnya, lihat Konversi Standar. Konversi yang ditentukan pengguna melakukan konversi antara jenis yang ditentukan pengguna, atau antara jenis yang ditentukan pengguna dan jenis bawaan. Anda dapat menerapkannya sebagai konstruktor Konversi atau sebagai fungsi Konversi.
Konversi dapat menjadi eksplisit—ketika programmer memanggil satu jenis untuk dikonversi ke jenis lain, seperti dalam pemeran atau inisialisasi langsung—atau implisit—ketika bahasa atau program memanggil jenis yang berbeda dari yang diberikan oleh programmer.
Konversi implisit dicoba ketika:
Argumen yang diberikan ke fungsi tidak memiliki jenis yang sama dengan parameter yang cocok.
Nilai yang dikembalikan dari fungsi tidak memiliki jenis yang sama dengan jenis pengembalian fungsi.
Ekspresi penginisialisasi tidak memiliki jenis yang sama dengan objek yang diinisialisasi.
Ekspresi yang mengontrol pernyataan kondisional, konstruksi perulangan, atau sakelar tidak memiliki jenis hasil yang diperlukan untuk mengontrolnya.
Operand yang disediakan ke operator tidak memiliki jenis yang sama dengan parameter operand yang cocok. Untuk operator bawaan, kedua operan harus memiliki jenis yang sama, dan dikonversi ke jenis umum yang dapat mewakili keduanya. Untuk informasi selengkapnya, lihat Konversi Standar. Untuk operator yang ditentukan pengguna, setiap operand harus memiliki jenis yang sama dengan parameter operand yang cocok.
Ketika satu konversi standar tidak dapat menyelesaikan konversi implisit, pengkompilasi dapat menggunakan konversi yang ditentukan pengguna, diikuti secara opsional oleh konversi standar tambahan, untuk menyelesaikannya.
Ketika dua atau beberapa konversi yang ditentukan pengguna yang melakukan konversi yang sama tersedia di situs konversi, konversi dikatakan ambigu. Ambiguitas tersebut adalah kesalahan karena pengkompilasi tidak dapat menentukan salah satu konversi yang tersedia yang harus dipilihnya. Namun, ini bukan kesalahan hanya untuk menentukan beberapa cara untuk melakukan konversi yang sama karena kumpulan konversi yang tersedia dapat berbeda di lokasi yang berbeda dalam kode sumber—misalnya, tergantung pada file header mana yang disertakan dalam file sumber. Selama hanya satu konversi yang tersedia di situs konversi, tidak ada ambiguitas. Ada beberapa cara agar konversi ambigu dapat muncul, tetapi yang paling umum adalah:
Beberapa warisan. Konversi didefinisikan dalam lebih dari satu kelas dasar.
Panggilan fungsi ambigu. Konversi didefinisikan sebagai konstruktor konversi dari jenis target dan sebagai fungsi konversi dari jenis sumber. Untuk informasi selengkapnya, lihat Fungsi konversi.
Anda biasanya dapat menyelesaikan ambiguitas hanya dengan memenuhi syarat nama jenis yang terlibat lebih lengkap atau dengan melakukan cast eksplisit untuk mengklarifikasi niat Anda.
Baik konstruktor konversi maupun fungsi konversi mematuhi aturan kontrol akses anggota, tetapi aksesibilitas konversi hanya dipertimbangkan jika dan kapan konversi yang tidak ambigu dapat ditentukan. Ini berarti bahwa konversi dapat ambigu meskipun tingkat akses konversi yang bersaing akan mencegahnya digunakan. Untuk informasi selengkapnya tentang aksesibilitas anggota, lihat Kontrol Akses Anggota.
Kata kunci eksplisit dan masalah dengan konversi implisit
Secara default saat Anda membuat konversi yang ditentukan pengguna, pengkompilasi dapat menggunakannya untuk melakukan konversi implisit. Terkadang ini yang Anda inginkan, tetapi di lain waktu aturan sederhana yang memandu pengompilasi dalam membuat konversi implisit dapat menyebabkannya menerima kode yang tidak Anda inginkan.
Salah satu contoh terkenal dari konversi implisit yang dapat menyebabkan masalah adalah konversi ke bool
. Ada banyak alasan bahwa Anda mungkin ingin membuat jenis kelas yang dapat digunakan dalam konteks Boolean—misalnya, sehingga dapat digunakan untuk mengontrol if
pernyataan atau perulangan—tetapi ketika pengompilasi melakukan konversi yang ditentukan pengguna ke jenis bawaan, pengompilasi diizinkan untuk menerapkan konversi standar tambahan setelahnya. Niat dari konversi standar tambahan ini adalah untuk memungkinkan hal-hal seperti promosi dari short
ke int
, tetapi juga membuka pintu untuk konversi yang kurang jelas—misalnya, dari bool
ke int
, yang memungkinkan jenis kelas Anda digunakan dalam konteks bilangan bulat yang tidak pernah Anda maksudkan. Masalah khusus ini dikenal sebagai Masalah Bool Aman. Masalah semacam ini adalah di mana explicit
kata kunci dapat membantu.
Kata explicit
kunci memberi tahu pengkompilasi bahwa konversi yang ditentukan tidak dapat digunakan untuk melakukan konversi implisit. Jika Anda menginginkan kenyamanan sinaks dari konversi implisit sebelum explicit
kata kunci diperkenalkan, Anda harus menerima konsekuensi yang tidak diinginkan yang kadang-kadang dibuat atau menggunakan fungsi konversi bernama yang kurang nyaman sebagai solusinya. Sekarang, dengan menggunakan explicit
kata kunci, Anda dapat membuat konversi nyaman yang hanya dapat digunakan untuk melakukan pemeran eksplisit atau inisialisasi langsung, dan itu tidak akan menyebabkan jenis masalah yang dicontohkan oleh Masalah Bool Aman.
Kata explicit
kunci dapat diterapkan ke konstruktor konversi sejak C++98, dan ke fungsi konversi sejak C++11. Bagian berikut berisi informasi selengkapnya tentang cara menggunakan explicit
kata kunci.
Konstruktor konversi
Konstruktor konversi menentukan konversi dari jenis yang ditentukan pengguna atau bawaan ke jenis yang ditentukan pengguna. Contoh berikut menunjukkan konstruktor konversi yang mengonversi dari jenis double
bawaan ke jenis Money
yang ditentukan pengguna .
#include <iostream>
class Money
{
public:
Money() : amount{ 0.0 } {};
Money(double _amount) : amount{ _amount } {};
double amount;
};
void display_balance(const Money balance)
{
std::cout << "The balance is: " << balance.amount << std::endl;
}
int main(int argc, char* argv[])
{
Money payable{ 79.99 };
display_balance(payable);
display_balance(49.95);
display_balance(9.99f);
return 0;
}
Perhatikan bahwa panggilan pertama ke fungsi display_balance
, yang mengambil argumen jenis Money
, tidak memerlukan konversi karena argumennya adalah jenis yang benar. Namun, pada panggilan kedua ke display_balance
, konversi diperlukan karena jenis argumen, double
dengan nilai 49.95
, bukan yang diharapkan fungsi. Fungsi tidak dapat menggunakan nilai ini secara langsung, tetapi karena ada konversi dari jenis argumen—double
ke jenis parameter yang cocok—Money
—nilai sementara dari jenis Money
dibuat dari argumen dan digunakan untuk menyelesaikan panggilan fungsi. Dalam panggilan ketiga ke display_balance
, perhatikan bahwa argumen bukan double
, tetapi sebaliknya dengan float
nilai 9.99
—namun panggilan fungsi masih dapat diselesaikan karena pengkompilasi dapat melakukan konversi standar—dalam hal ini, dari float
ke double
—dan kemudian melakukan konversi yang ditentukan pengguna dari double
ke Money
untuk menyelesaikan konversi yang diperlukan.
Mendeklarasikan konstruktor konversi
Aturan berikut berlaku untuk mendeklarasikan konstruktor konversi:
Jenis target konversi adalah jenis yang ditentukan pengguna yang sedang dibangun.
Konstruktor konversi biasanya mengambil tepat satu argumen, yang merupakan jenis sumber. Namun, konstruktor konversi dapat menentukan parameter tambahan jika setiap parameter tambahan memiliki nilai default. Jenis sumber tetap menjadi jenis parameter pertama.
Konstruktor konversi, seperti semua konstruktor, tidak menentukan jenis pengembalian. Menentukan jenis pengembalian dalam deklarasi adalah kesalahan.
Konstruktor konversi bisa eksplisit.
Konstruktor konversi eksplisit
Dengan mendeklarasikan konstruktor konversi menjadi explicit
, konstruktor hanya dapat digunakan untuk melakukan inisialisasi langsung objek atau untuk melakukan cast eksplisit. Ini mencegah fungsi yang menerima argumen jenis kelas juga secara implisit menerima argumen jenis sumber konstruktor konversi, dan mencegah jenis kelas diinisialisasi dari nilai jenis sumber. Contoh berikut menunjukkan cara menentukan konstruktor konversi eksplisit, dan efek yang dimilikinya pada kode apa yang terbentuk dengan baik.
#include <iostream>
class Money
{
public:
Money() : amount{ 0.0 } {};
explicit Money(double _amount) : amount{ _amount } {};
double amount;
};
void display_balance(const Money balance)
{
std::cout << "The balance is: " << balance.amount << std::endl;
}
int main(int argc, char* argv[])
{
Money payable{ 79.99 }; // Legal: direct initialization is explicit.
display_balance(payable); // Legal: no conversion required
display_balance(49.95); // Error: no suitable conversion exists to convert from double to Money.
display_balance((Money)9.99f); // Legal: explicit cast to Money
return 0;
}
Dalam contoh ini, perhatikan bahwa Anda masih dapat menggunakan konstruktor konversi eksplisit untuk melakukan inisialisasi langsung .payable
Jika sebaliknya Anda menyalin-menginisialisasi Money payable = 79.99;
, itu akan menjadi kesalahan. Panggilan pertama ke display_balance
tidak terpengaruh karena argumen adalah jenis yang benar. Panggilan kedua ke display_balance
adalah kesalahan, karena konstruktor konversi tidak dapat digunakan untuk melakukan konversi implisit. Panggilan ketiga ke display_balance
legal karena cast eksplisit ke Money
, tetapi perhatikan bahwa kompilator masih membantu menyelesaikan pemeran dengan memasukkan cast implisit dari float
ke double
.
Meskipun kenyamanan memungkinkan konversi implisit dapat menggoda, melakukannya dapat memperkenalkan bug yang sulit ditemukan. Aturan praktisnya adalah membuat semua konstruktor konversi eksplisit kecuali ketika Anda yakin bahwa Anda ingin konversi tertentu terjadi secara implisit.
Fungsi konversi
Fungsi konversi menentukan konversi dari jenis yang ditentukan pengguna ke jenis lain. Fungsi-fungsi ini kadang-kadang disebut sebagai "operator transmisi" karena, bersama dengan konstruktor konversi, dipanggil ketika nilai dilemparkan ke jenis yang berbeda. Contoh berikut menunjukkan fungsi konversi yang mengonversi dari jenis yang ditentukan pengguna, Money
, ke jenis bawaan, double
:
#include <iostream>
class Money
{
public:
Money() : amount{ 0.0 } {};
Money(double _amount) : amount{ _amount } {};
operator double() const { return amount; }
private:
double amount;
};
void display_balance(const Money balance)
{
std::cout << "The balance is: " << balance << std::endl;
}
Perhatikan bahwa variabel amount
anggota dibuat privat dan bahwa fungsi konversi publik ke jenis double
diperkenalkan hanya untuk mengembalikan nilai amount
. Dalam fungsi display_balance
, konversi implisit terjadi ketika nilai balance
dialirkan ke output standar dengan menggunakan operator <<
penyisipan aliran . Karena tidak ada operator penyisipan aliran yang didefinisikan untuk jenis Money
yang ditentukan pengguna , tetapi ada satu untuk jenis double
bawaan , pengkompilasi dapat menggunakan fungsi konversi dari Money
ke untuk double
memenuhi operator penyisipan aliran.
Fungsi konversi diwariskan oleh kelas turunan. Fungsi konversi dalam kelas turunan hanya mengambil alih fungsi konversi yang diwariskan saat dikonversi ke jenis yang sama persis. Misalnya, fungsi konversi yang ditentukan pengguna dari int operator kelas turunan tidak mengambil alih—atau bahkan memengaruhi—fungsi konversi yang ditentukan pengguna dari operator kelas dasar singkat, meskipun konversi standar menentukan hubungan konversi antara int
dan short
.
Mendeklarasikan fungsi konversi
Aturan berikut berlaku untuk mendeklarasikan fungsi konversi:
Jenis target konversi harus dideklarasikan sebelum deklarasi fungsi konversi. Kelas, struktur, enumerasi, dan typedef tidak dapat dideklarasikan dalam deklarasi fungsi konversi.
operator struct String { char string_storage; }() // illegal
Fungsi konversi tidak mengambil argumen. Menentukan parameter apa pun dalam deklarasi adalah kesalahan.
Fungsi konversi memiliki jenis pengembalian yang ditentukan oleh nama fungsi konversi, yang juga merupakan nama jenis target konversi. Menentukan jenis pengembalian dalam deklarasi adalah kesalahan.
Fungsi konversi bisa virtual.
Fungsi konversi bisa eksplisit.
Fungsi konversi eksplisit
Ketika fungsi konversi dinyatakan eksplisit, fungsi tersebut hanya dapat digunakan untuk melakukan cast eksplisit. Ini mencegah fungsi yang menerima argumen jenis target fungsi konversi juga secara implisit menerima argumen jenis kelas, dan mencegah instans jenis target diinisialisasi dari nilai jenis kelas. Contoh berikut menunjukkan cara menentukan fungsi konversi eksplisit dan efeknya pada kode apa yang terbentuk dengan baik.
#include <iostream>
class Money
{
public:
Money() : amount{ 0.0 } {};
Money(double _amount) : amount{ _amount } {};
explicit operator double() const { return amount; }
private:
double amount;
};
void display_balance(const Money balance)
{
std::cout << "The balance is: " << (double)balance << std::endl;
}
Di sini operator fungsi konversi ganda telah dibuat eksplisit, dan cast eksplisit ke jenis double
telah diperkenalkan dalam fungsi display_balance
untuk melakukan konversi. Jika transmisi ini dihilangkan, pengkompilasi tidak akan dapat menemukan operator <<
penyisipan aliran yang sesuai untuk jenis Money
dan kesalahan akan terjadi.