Pertanyaan Pemain reguler vs. static_cast vs. dynamic_cast [duplikat]


Pertanyaan ini sudah memiliki jawaban di sini:

Saya sudah menulis kode C dan C ++ selama hampir dua puluh tahun, tetapi ada satu aspek dari bahasa-bahasa ini yang saya tidak pernah benar-benar mengerti. Saya sudah jelas menggunakan gips biasa yaitu

MyClass *m = (MyClass *)ptr;

di semua tempat, tetapi tampaknya ada dua jenis gips lainnya, dan saya tidak tahu bedanya. Apa perbedaan antara baris kode berikut?

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);

1468
2017-08-26 13:20


asal


Jawaban:


static_cast

static_cast digunakan untuk kasus-kasus di mana pada dasarnya Anda ingin membalikkan konversi implisit, dengan beberapa pembatasan dan penambahan. static_cast tidak melakukan pemeriksaan runtime. Ini harus digunakan jika Anda tahu bahwa Anda mengacu pada objek dari tipe tertentu, dan dengan demikian pemeriksaan tidak diperlukan. Contoh:

void func(void *data) {
  // Conversion from MyClass* -> void* is implicit
  MyClass *c = static_cast<MyClass*>(data);
  ...
}

int main() {
  MyClass c;
  start_thread(&func, &c)  // func(&c) will be called
      .join();
}

Dalam contoh ini, Anda tahu bahwa Anda telah lulus MyClass objek, dan dengan demikian tidak ada kebutuhan untuk pemeriksaan runtime untuk memastikan ini.

dynamic_cast

dynamic_cast berguna ketika Anda tidak tahu apa jenis dinamis dari objek tersebut. Ini mengembalikan pointer null jika objek yang dimaksud tidak berisi tipe yang dicor sebagai kelas dasar (saat Anda mentransmisikan ke referensi, bad_cast pengecualian dilemparkan dalam kasus itu).

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

Anda tidak dapat menggunakan dynamic_cast jika Anda downcast (cast ke kelas turunan) dan tipe argumen tidak polimorfik. Misalnya, kode berikut tidak valid, karena Base tidak mengandung fungsi virtual apa pun:

struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

Sebuah "up-cast" (dilemparkan ke kelas dasar) selalu berlaku dengan keduanya static_cast dan dynamic_cast, dan juga tanpa pemain, sebagai "up-cast" adalah konversi implisit.

Pemain Biasa

Gips ini juga disebut C-style cast. Corak C-style pada dasarnya sama dengan mencoba berbagai urutan c ++ cast, dan mengambil c ++ cast pertama yang bekerja, tanpa pernah mempertimbangkan dynamic_cast. Tak perlu dikatakan, ini jauh lebih kuat karena menggabungkan semua const_cast, static_cast dan reinterpret_cast, tetapi juga tidak aman, karena tidak digunakan dynamic_cast.

Selain itu, corak C-style tidak hanya memungkinkan Anda untuk melakukan ini, tetapi mereka juga memungkinkan Anda untuk mentransmisikan dengan aman ke kelas dasar pribadi, sementara "setara" static_cast urutan akan memberi Anda kesalahan waktu kompilasi untuk itu.

Beberapa orang lebih memilih gaya-C gips karena singkatnya mereka. Saya menggunakannya hanya untuk gips numerik, dan menggunakan gips C ++ yang tepat ketika jenis yang ditentukan pengguna dilibatkan, karena mereka memberikan pemeriksaan yang lebih ketat.


1413
2017-08-10 13:50



Gips statis

Pemeran statis melakukan konversi antara jenis yang kompatibel. Ini mirip dengan pemain C-style, tetapi lebih restriktif. Sebagai contoh, pemain C-style akan memungkinkan pointer integer untuk menunjuk ke char.

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

Karena ini menghasilkan penunjuk 4-byte yang menunjuk ke 1 byte memori yang dialokasikan, menulis ke penunjuk ini akan menyebabkan kesalahan run-time atau akan menimpa beberapa memori yang berdekatan.

*p = 5; // run-time error: stack corruption

Berbeda dengan cast C-style, cast statis akan memungkinkan compiler untuk memeriksa bahwa tipe data pointer dan pointee kompatibel, yang memungkinkan programmer untuk menangkap penunjuk penunjuk yang salah ini selama kompilasi.

int *q = static_cast<int*>(&c); // compile-time error

Menafsirkan ulang cast

Untuk memaksa konversi penunjuk, dengan cara yang sama seperti pemain C-gaya lakukan di latar belakang, menafsirkan ulang gips akan digunakan sebagai gantinya.

int *r = reinterpret_cast<int*>(&c); // forced conversion

Cast ini menangani konversi antara jenis-jenis tertentu yang tidak terkait, seperti dari satu jenis pointer ke jenis pointer lain yang tidak kompatibel. Ini hanya akan melakukan salinan biner data tanpa mengubah pola bit yang mendasarinya. Perhatikan bahwa hasil dari operasi tingkat rendah adalah sistem-spesifik dan karena itu tidak portabel. Ini harus digunakan dengan hati-hati jika tidak dapat dihindari sama sekali.

Pemeran dinamis

Yang ini hanya digunakan untuk mengonversi pointer objek dan referensi objek ke jenis pointer atau referensi lain dalam hierarki warisan. Ini adalah satu-satunya pemeran yang memastikan bahwa objek yang dituju dapat dikonversi, dengan melakukan pemeriksaan run-time bahwa penunjuk mengacu pada objek lengkap dari tipe tujuan. Untuk pemeriksaan run-time ini menjadi mungkin objek harus polimorfik. Yaitu, kelas harus menentukan atau mewarisi setidaknya satu fungsi virtual. Ini karena kompiler hanya akan menghasilkan informasi jenis run-time yang dibutuhkan untuk objek tersebut.

Contoh cetakan dinamis

Dalam contoh di bawah ini, pointer MyChild diubah menjadi pointer MyBase menggunakan cor dinamis. Konversi yang diturunkan ke basis ini berhasil, karena objek Anak mencakup objek Basis lengkap.

class MyBase 
{ 
  public:
  virtual void test() {}
};
class MyChild : public MyBase {};



int main()
{
  MyChild *child = new MyChild();
  MyBase  *base = dynamic_cast<MyBase*>(child); // ok
}

Contoh berikut mencoba untuk mengkonversi pointer MyBase ke penunjuk MyChild. Karena objek Basis tidak berisi objek Anak lengkap konversi penunjuk ini akan gagal. Untuk menunjukkan ini, cor dinamis mengembalikan pointer null. Ini memberikan cara yang mudah untuk memeriksa apakah konversi telah berhasil selama waktu berjalan.

MyBase  *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);


if (child == 0) 
std::cout << "Null pointer returned";

Jika referensi diubah alih-alih pointer, maka pemain dinamis akan gagal dengan melempar pengecualian bad_cast. Ini perlu ditangani menggunakan pernyataan try-catch.

#include <exception>
// …  
try
{ 
  MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e) 
{ 
  std::cout << e.what(); // bad dynamic_cast
}

Pemeran dinamis atau statis

Keuntungan menggunakan cast dinamis adalah memungkinkan programmer untuk memeriksa apakah konversi telah berhasil selama waktu berjalan. Kerugiannya adalah bahwa ada overhead kinerja yang terkait dengan melakukan pemeriksaan ini. Untuk alasan ini menggunakan gips statis akan lebih disukai dalam contoh pertama, karena konversi turunan ke basis tidak akan pernah gagal.

MyBase *base = static_cast<MyBase*>(child); // ok

Namun, dalam contoh kedua, konversi dapat berhasil atau gagal. Ini akan gagal jika objek MyBase berisi instance MyBase dan itu akan berhasil jika berisi instance MyChild. Dalam beberapa situasi, ini mungkin tidak diketahui hingga waktu berjalan. Saat ini, casing dinamis adalah pilihan yang lebih baik daripada gips statis.

// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);

Jika konversi basis-ke-diturunkan telah dilakukan dengan menggunakan gips statis daripada gips dinamis, konversi tidak akan gagal. Itu akan mengembalikan pointer yang mengacu pada objek yang tidak lengkap. Dereferencing seperti pointer dapat menyebabkan kesalahan run-time.

// Allowed, but invalid
MyChild *child = static_cast<MyChild*>(base);

// Incomplete MyChild object dereferenced
(*child);

Pemeran Const

Yang ini terutama digunakan untuk menambah atau menghapus pengubah konstitusi dari suatu variabel.

const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

Meskipun const cast memungkinkan nilai dari sebuah konstanta untuk diubah, hal ini masih merupakan kode yang tidak valid yang dapat menyebabkan kesalahan run-time. Ini bisa terjadi misalnya jika konstanta terletak di bagian memori read-only.

*nonConst = 10; // potential run-time error

Const cast digunakan terutama ketika ada fungsi yang mengambil argumen pointer non-konstan, meskipun tidak memodifikasi pointe.

void print(int *p) 
{
   std::cout << *p;
}

Fungsi ini kemudian dapat dilewatkan variabel konstan dengan menggunakan const cast.

print(&myConst); // error: cannot convert 
                 // const int* to int*

print(nonConst); // allowed

Sumber dan Penjelasan Lainnya


116
2017-08-26 13:28



Anda harus melihat artikel itu C ++ Pemrograman / Pengecoran Jenis.

Ini berisi deskripsi yang baik dari semua jenis pemain yang berbeda. Berikut diambil dari tautan di atas:

const_cast

const_cast (expression) The const_cast <> () digunakan untuk menambah / menghapus   const (ness) (atau volatile-ness) dari suatu variabel.

static_cast

static_cast (expression) static_cast <> () digunakan untuk mentransmisikan antara   tipe integer. 'misalnya.' char-> panjang, int-> singkat dll.

Gips statis juga digunakan untuk melemparkan pointer ke jenis yang terkait, untuk   contoh casting void * ke tipe yang sesuai.

dynamic_cast

Cast dinamis digunakan untuk mengkonversi pointer dan referensi pada saat run-time,   umumnya untuk tujuan mentransmisikan pointer atau referensi ke atas atau ke bawah   rantai warisan (hierarki warisan).

dynamic_cast (ekspresi)

Jenis target harus berupa pointer atau tipe referensi, dan   ekspresi harus dievaluasi ke penunjuk atau referensi. Pemeran dinamis bekerja   hanya ketika jenis objek yang merujuk merujuk adalah   kompatibel dengan tipe target dan kelas dasar memiliki setidaknya satu   fungsi anggota virtual. Jika tidak, dan jenis ekspresi yang dilemparkan   adalah pointer, NULL dikembalikan, jika cor dinamis pada referensi   gagal, pengecualian bad_cast dibuang. Ketika tidak gagal, dinamis   cor mengembalikan pointer atau referensi dari tipe target ke objek   ke mana ekspresi dimaksud.

reinterpret_cast

Menafsirkan ulang hanya melemparkan satu jenis bitwise ke yang lain. Penunjuk apa pun   atau tipe integral dapat dicor ke yang lain dengan menafsirkan kembali pemain,   mudah memungkinkan untuk disalahgunakan. Misalnya, dengan menafsirkan kembali satu   mungkin, tidak aman, mentransmisikan pointer integer ke penunjuk string.


71
2017-09-19 17:30



Hindari menggunakan gips C-Style.

C-gaya gips adalah campuran dari const dan menafsirkan kembali cast, dan sulit untuk menemukan-dan-ganti dalam kode Anda. Seorang programmer aplikasi C ++ harus menghindari cast C-style.


23
2017-08-26 13:39



FYI, saya yakin Bjarne Stroustrup dikutip mengatakan bahwa pemain C-style harus dihindari dan Anda harus menggunakan static_cast atau dynamic_cast jika memungkinkan.

C ++ style FAQ Barne Stroustrup

Ambillah nasihat itu untuk apa yang Anda inginkan. Saya jauh dari seorang guru C ++.


21
2017-08-26 13:38



C-style casts conflate const_cast, static_cast, dan reinterpret_cast.

Saya berharap C ++ tidak memiliki cast C-style. C ++ gips menonjol dengan benar (sebagaimana mestinya; gips biasanya menunjukkan melakukan sesuatu yang buruk) dan membedakan dengan tepat antara berbagai jenis konversi yang dilakukan pemain. Mereka juga mengizinkan fungsi yang tampak serupa untuk ditulis, misalnya boost :: lexical_cast, yang cukup bagus dari perspektif konsistensi.


10
2017-08-26 13:26



dynamic_castmemiliki pengecekan tipe runtime dan hanya bekerja dengan referensi dan pointer, sedangkan static_cast tidak menawarkan pengecekan tipe runtime. Untuk informasi lengkap, lihat artikel MSDN Operator static_cast.


8
2018-02-05 17:10



dynamic_cast hanya mendukung jenis penunjuk dan referensi. Ia kembali NULL jika pemain tidak mungkin jika jenisnya adalah pointer atau melempar pengecualian jika jenisnya adalah tipe referensi. Karenanya, dynamic_cast dapat digunakan untuk memeriksa apakah suatu objek adalah tipe yang diberikan, static_cast tidak bisa (Anda hanya akan berakhir dengan nilai yang tidak valid).

C-gaya (dan lainnya) gips telah tercakup dalam jawaban lainnya.


7