Pertanyaan Kapan static_cast, dynamic_cast, const_cast dan reinterpret_cast digunakan?


Apa kegunaan yang tepat dari:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • C-style cast (type)value
  • Pemain gaya-fungsi type(value)

Bagaimana orang memutuskan mana yang akan digunakan di mana kasus-kasus tertentu?


2048
2017-12-01 20:11


asal


Jawaban:


static_cast adalah pemain pertama yang harus Anda coba gunakan. Itu melakukan hal-hal seperti konversi implisit antar jenis (seperti int untuk float, atau penunjuk ke void*), dan itu juga dapat memanggil fungsi konversi eksplisit (atau yang implisit). Dalam banyak kasus, secara eksplisit menyatakan static_cast tidak perlu, tetapi penting untuk dicatat bahwa T(something) sintaks setara dengan (T)something dan harus dihindari (lebih lanjut nanti). SEBUAH T(something, something_else) aman, bagaimanapun, dan dijamin untuk memanggil konstruktor.

static_cast juga bisa dilemparkan melalui hierarki warisan. Hal ini tidak diperlukan saat melakukan casting ke atas (menuju kelas dasar), tetapi ketika casting ke bawah dapat digunakan selama tidak dilemparkan virtual warisan. Itu tidak melakukan pemeriksaan, bagaimanapun, dan itu adalah perilaku tidak terdefinisi static_cast bawah hierarki ke tipe yang sebenarnya bukan tipe objek.


const_cast dapat digunakan untuk menghapus atau menambahkan const ke variabel; tidak ada cast C ++ lain yang mampu menghapusnya (bahkan tidak reinterpret_cast). Penting untuk dicatat bahwa memodifikasi sebelumnya const nilai hanya tidak terdefinisi jika variabel aslinya adalah const; jika Anda menggunakannya untuk mengambil const dari referensi ke sesuatu yang tidak diumumkan const, itu aman. Ini dapat berguna saat memberatkan fungsi anggota berdasarkan const, contohnya. Ini juga dapat digunakan untuk menambahkan const ke suatu objek, seperti memanggil fungsi anggota yang berlebihan.

const_cast juga berfungsi sama volatile, meskipun itu kurang umum.


dynamic_cast hampir secara eksklusif digunakan untuk menangani polimorfisme. Anda dapat mentransmisikan pointer atau referensi ke jenis polimorfik ke jenis kelas lainnya (tipe polimorfik memiliki setidaknya satu fungsi virtual, dideklarasikan atau diwarisi). Anda dapat menggunakannya untuk lebih dari sekadar mentransmisikan ke bawah - Anda dapat mentransmisi ke samping atau bahkan ke rantai lain. Itu dynamic_cast akan mencari objek yang diinginkan dan mengembalikannya jika memungkinkan. Jika tidak bisa, itu akan kembali nullptr dalam hal penunjuk, atau lemparan std::bad_cast dalam kasus referensi.

dynamic_cast memiliki beberapa keterbatasan. Ini tidak berfungsi jika ada beberapa objek dari jenis yang sama dalam hierarki warisan (yang disebut 'berlian yang ditakuti') dan Anda tidak menggunakan virtual warisan. Itu juga hanya bisa melalui pewarisan publik - itu akan selalu gagal melakukan perjalanan protected atau private warisan. Namun, ini jarang menjadi masalah, karena bentuk pewarisan seperti itu jarang terjadi.


reinterpret_cast adalah pemain paling berbahaya, dan harus digunakan dengan sangat hemat. Ternyata satu jenis langsung ke yang lain - seperti casting nilai dari satu pointer ke yang lain, atau menyimpan pointer dalam int, atau segala macam hal buruk lainnya. Sebagian besar, satu-satunya jaminan yang Anda dapatkan reinterpret_cast itu biasanya jika Anda memberikan hasilnya kembali ke tipe asli, Anda akan mendapatkan nilai yang sama persis (tapi tidak jika tipe menengah lebih kecil dari tipe asli). Ada sejumlah konversi itu reinterpret_cast tidak bisa melakukannya juga. Ini digunakan terutama untuk konversi aneh dan manipulasi bit, seperti mengubah aliran data mentah menjadi data aktual, atau menyimpan data dalam bit rendah dari pointer yang sejajar.


C-style cast dan corak gaya-fungsi gips menggunakan (type)object atau type(object), masing-masing. Pemain C-style didefinisikan sebagai yang pertama dari yang berikut yang berhasil:

  • const_cast
  • static_cast (meskipun mengabaikan pembatasan akses)
  • static_cast (lihat di atas), lalu const_cast
  • reinterpret_cast
  • reinterpret_cast, kemudian const_cast

Oleh karena itu dapat digunakan sebagai pengganti gips lain dalam beberapa kasus, tetapi bisa sangat berbahaya karena kemampuan untuk berpindah ke reinterpret_cast, dan yang terakhir harus lebih disukai ketika casting eksplisit diperlukan, kecuali Anda yakin static_cast akan berhasil atau reinterpret_cast akan gagal. Bahkan kemudian, pertimbangkan opsi yang lebih panjang dan lebih eksplisit.

Gaya C-cast juga mengabaikan kontrol akses saat melakukan static_cast, yang berarti bahwa mereka memiliki kemampuan untuk melakukan operasi yang tidak dapat dilakukan oleh pemain lain. Ini sebagian besar adalah sebuah kludge, meskipun, dan dalam pikiran saya hanyalah alasan lain untuk menghindari pemain C-style.


2207
2017-12-01 20:22



Menggunakan dynamic_cast untuk mengubah pointer / referensi dalam hierarki warisan.

Menggunakan static_cast untuk konversi tipe biasa.

Menggunakan reinterpret_cast untuk reinterpretasi tingkat rendah dari pola bit. Gunakan dengan sangat hati-hati.

Menggunakan const_cast untuk mengusir const/volatile. Hindari ini kecuali Anda terjebak menggunakan API yang salah.


283
2018-01-21 04:53



(Banyak penjelasan teoritis dan konseptual telah diberikan di atas) 

Di bawah ini adalah beberapa contoh-contoh praktis ketika saya digunakan static_cast, dynamic_cast, const_cast, reinterpret_cast.

(Juga rujuk ini untuk memahami penjelasannya: http://www.cplusplus.com/doc/tutorial/typecasting/)

static_cast:

OnEventData(void* pData)

{
  ......

  //  pData is a void* pData, 

  //  EventData is a structure e.g. 
  //  typedef struct _EventData {
  //  std::string id;
  //  std:: string remote_id;
  //  } EventData;

  // On Some Situation a void pointer *pData
  // has been static_casted as 
  // EventData* pointer 

  EventData *evtdata = static_cast<EventData*>(pData);
  .....
}

dynamic_cast:

void DebugLog::OnMessage(Message *msg)
{
    static DebugMsgData *debug;
    static XYZMsgData *xyz;

    if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
        // debug message
    }
    else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
        // xyz message
    }
    else/* if( ... )*/{
        // ...
    }
}

const_cast:

// *Passwd declared as a const

const unsigned char *Passwd


// on some situation it require to remove its constness

const_cast<unsigned char*>(Passwd)

reinterpret_cast:

typedef unsigned short uint16;

// Read Bytes returns that 2 bytes got read. 

bool ByteBuffer::ReadUInt16(uint16& val) {
  return ReadBytes(reinterpret_cast<char*>(&val), 2);
}

151
2017-12-11 02:05



Mungkin membantu jika Anda tahu sedikit internal ...

static_cast

  • C ++ compiler sudah tahu cara mengonversi jenis scaler seperti float ke int. Gunakan static_cast untuk mereka.
  • Secara umum, ketika mengkonversi tipe A ke B, static_cast akan memanggil konstruktor B yang melaluinya A. Jika B tidak memiliki konstruktor seperti itu maka Anda mendapatkan kesalahan waktu kompilasi.
  • Transmisikan dari A* untuk B* selalu berhasil jika A dan B berada dalam hierarki warisan (atau batal) jika tidak Anda mendapatkan kesalahan kompilasi.
  • Gotcha: Jika Anda mentransmisikan pointer basis ke pointer turunan tetapi jika objek sebenarnya adalah jika bukan tipe turunan maka Anda tidak mendapatkan kesalahan. Anda mendapatkan penunjuk yang buruk dan segera setelah Anda mencoba mengakses anggota penunjuk asal, Anda mendapatkan segfault saat runtime.
  • Sama berlaku untuk A& untuk B&.
  • Gotcha: Pemain dari Derived ke Base atau viceversa membuat salinan baru! Bagi orang-orang yang datang dari C # / Java, banyak hal di atas dapat menjadi kejutan besar.

dynamic_cast

  • dynamic_cast menggunakan informasi jenis runtime untuk mencari tahu apakah cast valid. Sebagai contoh, (Base*) untuk (Derived*)mungkin gagal jika penunjuk tidak benar-benar dari tipe turunan.
  • Ini berarti, dynamic_cast sangat mahal dibandingkan dengan static_cast!
  • Untuk A* untuk B*, jika cast tidak valid maka dynamic_cast akan mengembalikan nullptr.
  • Untuk A& untuk B& jika cast tidak valid maka dynamic_cast akan membuang pengecualian bad_cast.
  • Tidak seperti pemain lain, ada overhead runtime.

const_cast

  • Sementara static_cast dapat melakukan non-const ke const itu tidak bisa pergi dengan cara lain. The const_cast dapat melakukan keduanya.
  • Salah satu contoh di mana ini berguna adalah iterasi melalui beberapa wadah seperti set<T> yang hanya mengembalikan elemennya sebagai const untuk memastikan Anda tidak mengubah kuncinya. Namun jika maksud Anda adalah untuk memodifikasi anggota non-kunci objek maka itu harusnya baik-baik saja. Anda dapat menggunakan const_cast untuk menghapus kekompakan.
  • Contoh lain adalah ketika Anda ingin menerapkan T& foo() sebaik const T& foo(). Untuk menghindari duplikasi kode, Anda dapat menerapkan const_cast untuk mengembalikan nilai satu fungsi dari fungsi lainnya.

reinterpret_cast

  • Ini pada dasarnya mengatakan bahwa mengambil byte ini di lokasi memori ini dan menganggapnya sebagai objek yang diberikan.
  • Misalnya, Anda dapat memuat 4 byte float ke 4 byte int untuk melihat bagaimana bit di float tampak seperti.
  • Tentunya, jika data tidak benar untuk jenisnya, Anda bisa mendapatkan segfault.
  • Tidak ada overhead runtime untuk pemain ini.

51
2017-12-01 20:20



Apakah ini jawab pertanyaanmu?

Saya tidak pernah digunakan reinterpret_cast, dan bertanya-tanya apakah berlari ke casing yang membutuhkannya bukanlah bau desain yang buruk. Di basis kode saya bekerja dynamic_cast banyak digunakan. Perbedaannya dengan static_cast Apakah itu dynamic_cast apakah pemeriksaan runtime yang mungkin (lebih aman) atau tidak mungkin (lebih banyak overhead) menjadi yang Anda inginkan (lihat msdn).


11
2018-05-31 14:16



Selain jawaban lain sejauh ini, di sini adalah contoh yang tidak diketahui di mana static_cast tidak cukup untuk itu reinterpret_cast diperlukan. Anggaplah ada fungsi yang dalam parameter output mengembalikan pointer ke objek dari kelas yang berbeda (yang tidak berbagi kelas dasar umum). Contoh nyata dari fungsi tersebut adalah CoCreateInstance() (lihat parameter terakhir, yang sebenarnya void**). Misalkan Anda meminta kelas objek tertentu dari fungsi ini, maka Anda tahu terlebih dahulu jenis untuk penunjuk (yang sering Anda lakukan untuk objek COM). Dalam hal ini Anda tidak dapat mentransmisikan pointer ke pointer Anda void** dengan static_cast: yang Anda butuhkan reinterpret_cast<void**>(&yourPointer).

Dalam kode:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    //static_cast<void**>(&pNetFwPolicy2) would give a compile error
    reinterpret_cast<void**>(&pNetFwPolicy2) );

Namun, static_cast berfungsi untuk pointer sederhana (bukan pointer ke pointer), sehingga kode di atas dapat ditulis ulang untuk menghindari reinterpret_cast (dengan harga variabel ekstra) dengan cara berikut:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    &tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);

9