Hampir setiap sumber C ++ yang pernah saya lihat yang membahas hal semacam ini memberi tahu saya bahwa saya harus lebih memilih pendekatan polimorfik daripada menggunakan RTTI (identifikasi jenis run-time). Secara umum, saya menanggapi nasihat semacam ini dengan serius, dan akan mencoba dan memahami alasannya - lagipula, C ++ adalah binatang buas yang perkasa dan sulit dipahami secara mendalam. Namun, untuk pertanyaan khusus ini, saya masih bingung dan ingin melihat nasihat seperti apa yang dapat ditawarkan internet. Pertama, izinkan saya merangkum apa yang telah saya pelajari sejauh ini, dengan membuat daftar alasan umum yang dikutip mengapa RTTI "dianggap berbahaya":
Beberapa kompiler tidak menggunakannya / RTTI tidak selalu diaktifkan
Saya benar-benar tidak percaya argumen ini. Ini seperti mengatakan saya tidak boleh menggunakan fitur C ++ 14, karena ada kompiler di luar sana yang tidak mendukungnya. Namun, tidak ada yang akan melarang saya menggunakan fitur C ++ 14. Mayoritas proyek akan memiliki pengaruh atas kompiler yang mereka gunakan, dan bagaimana itu dikonfigurasi. Bahkan mengutip halaman manual gcc:
-fno-rtti
Nonaktifkan pembuatan informasi tentang setiap kelas dengan fungsi virtual untuk digunakan oleh fitur identifikasi tipe run-time C ++ (dynamic_cast dan typeid). Jika Anda tidak menggunakan bagian-bagian bahasa tersebut, Anda dapat menghemat ruang dengan menggunakan bendera ini. Perhatikan bahwa penanganan pengecualian menggunakan informasi yang sama, tetapi G ++ membuatnya sesuai kebutuhan. Operator dynamic_cast masih dapat digunakan untuk cast yang tidak memerlukan informasi jenis run-time, misalnya cast ke "void *" atau kelas dasar yang tidak ambigu.
Hal ini memberi tahu saya bahwa jika saya tidak menggunakan RTTI, saya dapat menonaktifkannya. Itu seperti mengatakan, jika Anda tidak menggunakan Boost, Anda tidak perlu menautkannya. Saya tidak perlu merencanakan kasus di mana seseorang sedang menyusun -fno-rtti
. Plus, kompiler akan gagal dengan keras dan jelas dalam kasus ini.
Ini membutuhkan memori ekstra / Bisa lambat
Setiap kali saya tergoda untuk menggunakan RTTI, itu berarti saya perlu mengakses beberapa jenis informasi atau sifat kelas saya. Jika saya menerapkan solusi yang tidak menggunakan RTTI, ini biasanya berarti saya harus menambahkan beberapa bidang ke kelas saya untuk menyimpan informasi ini, jadi argumen memori agak kosong (saya akan memberikan contoh ini lebih jauh).
Sebuah dynamic_cast memang bisa lambat. Biasanya ada cara untuk menghindari menggunakannya dalam situasi kritis kecepatan. Dan saya tidak melihat alternatifnya. Jawaban SO ini menyarankan penggunaan enum, yang didefinisikan di kelas dasar, untuk menyimpan tipe. Itu hanya berfungsi jika Anda mengetahui semua kelas turunan apriori Anda. Itu cukup besar "jika"!
Dari jawaban tersebut, tampaknya juga biaya RTTI juga tidak jelas. Orang yang berbeda mengukur hal yang berbeda.
Desain polimorfik yang elegan akan membuat RTTI tidak diperlukan
Ini adalah jenis nasihat yang saya anggap serius. Dalam kasus ini, saya tidak dapat menemukan solusi non-RTTI yang baik yang mencakup kasus penggunaan RTTI saya. Izinkan saya memberikan contoh:
Katakanlah saya sedang menulis perpustakaan untuk menangani grafik dari beberapa jenis objek. Saya ingin mengizinkan pengguna untuk membuat tipe mereka sendiri saat menggunakan perpustakaan saya (jadi metode enum tidak tersedia). Saya memiliki kelas dasar untuk node saya:
class node_base
{
public:
node_base();
virtual ~node_base();
std::vector< std::shared_ptr<node_base> > get_adjacent_nodes();
};
Sekarang, node saya bisa dari berbagai jenis. Bagaimana dengan ini:
class red_node : virtual public node_base
{
public:
red_node();
virtual ~red_node();
void get_redness();
};
class yellow_node : virtual public node_base
{
public:
yellow_node();
virtual ~yellow_node();
void set_yellowness(int);
};
Sial, mengapa tidak salah satu dari ini:
class orange_node : public red_node, public yellow_node
{
public:
orange_node();
virtual ~orange_node();
void poke();
void poke_adjacent_oranges();
};
Fungsi terakhir menarik. Berikut cara untuk menulisnya:
void orange_node::poke_adjacent_oranges()
{
auto adj_nodes = get_adjacent_nodes();
foreach(auto node, adj_nodes) {
// In this case, typeid() and static_cast might be faster
std::shared_ptr<orange_node> o_node = dynamic_cast<orange_node>(node);
if (o_node) {
o_node->poke();
}
}
}
Ini semua tampak jelas dan bersih. Saya tidak perlu mendefinisikan atribut atau metode di mana saya tidak membutuhkannya, kelas simpul dasar dapat tetap ramping dan kejam. Tanpa RTTI, dari mana saya harus memulai? Mungkin saya bisa menambahkan atribut node_type ke kelas dasar:
class node_base
{
public:
node_base();
virtual ~node_base();
std::vector< std::shared_ptr<node_base> > get_adjacent_nodes();
private:
std::string my_type;
};
Apakah std :: string merupakan ide yang bagus untuk suatu tipe? Mungkin tidak, tapi apa lagi yang bisa saya gunakan? Buat nomor dan harap belum ada orang lain yang menggunakannya? Juga, dalam kasus orange_node saya, bagaimana jika saya ingin menggunakan metode dari red_node dan yellow_node? Apakah saya harus menyimpan beberapa jenis per node? Sepertinya itu rumit.
Kesimpulan
Contoh ini tampaknya tidak terlalu rumit atau tidak biasa (saya sedang mengerjakan sesuatu yang serupa di pekerjaan saya, di mana node mewakili perangkat keras sebenarnya yang dikontrol melalui perangkat lunak, dan yang melakukan hal yang sangat berbeda tergantung pada apa mereka). Namun saya tidak tahu cara yang bersih untuk melakukan ini dengan templat atau metode lain. Harap dicatat bahwa saya mencoba untuk memahami masalahnya, bukan membela teladan saya. Saya membaca halaman-halaman seperti jawaban SO yang saya tautkan di atas dan halaman di Wikibooks ini sepertinya menyarankan saya menyalahgunakan RTTI, tetapi saya ingin mengetahui alasannya.
Jadi, kembali ke pertanyaan awal saya: Mengapa 'polimorfisme murni' lebih disukai daripada menggunakan RTTI?
node_base
adalah bagian dari perpustakaan dan pengguna akan membuat jenis simpul mereka sendiri. Kemudian mereka tidak dapat mengubah node_base
untuk mengizinkan solusi lain, jadi mungkin RTTI menjadi pilihan terbaik mereka. Di sisi lain, ada cara lain untuk mendesain pustaka semacam itu sehingga tipe node baru dapat masuk dengan lebih elegan tanpa perlu menggunakan RTTI (dan cara lain untuk mendesain tipe node baru juga).