Bu tür şeyleri tartışan gördüğüm hemen hemen her C ++ kaynağı, RTTI (çalışma zamanı türü tanımlama) kullanmak yerine polimorfik yaklaşımları tercih etmem gerektiğini söylüyor. Genel olarak, bu tür tavsiyeleri ciddiye alıyorum ve mantığını anlamaya çalışacağım - sonuçta, C ++ muazzam bir canavar ve derinlemesine anlaşılması zor. Ancak, bu özel soru için bir boşluk bırakıyorum ve internetin ne tür tavsiyeler verebileceğini görmek istiyorum. Öncelikle, RTTI'nin neden "zararlı" kabul edildiğini söyleyen yaygın nedenleri listeleyerek şimdiye kadar öğrendiklerimi özetleyeyim:
Bazı derleyiciler bunu kullanmıyor / RTTI her zaman etkin değil
Bu argümana gerçekten inanmıyorum. Bu, C ++ 14 özelliklerini kullanmamam gerektiğini söylemek gibi, çünkü onu desteklemeyen derleyiciler var. Yine de kimse beni C ++ 14 özelliklerini kullanmaktan caydırmaz. Projelerin çoğu, kullandıkları derleyici ve nasıl yapılandırıldığı üzerinde etkiye sahip olacaktır. Gcc kılavuz sayfasından alıntı yapsak bile:
-fno-rtti
C ++ çalışma zamanı tür tanımlama özellikleri (dynamic_cast ve typeid) tarafından kullanılmak üzere sanal işlevlerle her sınıf hakkında bilgi üretmeyi devre dışı bırakın. Dilin bu kısımlarını kullanmazsanız, bu bayrağı kullanarak biraz alan kazanabilirsiniz. İstisna işlemenin aynı bilgileri kullandığını, ancak G ++ 'nın gerektiğinde bunları oluşturduğunu unutmayın. Dinamik yayın operatörü, çalışma zamanı tip bilgisi gerektirmeyen yayınlar için, yani "void *" e veya kesin temel sınıflara yayınlar için hala kullanılabilir.
Ne bu bana söyler olmasıdır eğer ben RTTI kullanarak değilim, bunu devre dışı bırakabilir. Bu, Boost kullanmıyorsanız, ona bağlanmanız gerekmediğini söylemek gibi bir şey. Birinin derlediği vaka için plan yapmama gerek yok -fno-rtti
. Ayrıca, bu durumda derleyici yüksek sesle ve net bir şekilde başarısız olacaktır.
Ekstra belleğe mal olur / Yavaş olabilir
Ne zaman RTTI kullanmaya istekli olsam, bu, sınıfımın bir tür tip bilgisine veya özelliğine erişmem gerektiği anlamına geliyor. RTTI kullanmayan bir çözüm uygularsam, bu genellikle bu bilgiyi depolamak için sınıflarıma bazı alanlar eklemem gerektiği anlamına gelir, bu nedenle bellek argümanı bir çeşit geçersizdir (bunun bir örneğini daha aşağıda vereceğim).
Dinamik yayın gerçekten yavaş olabilir. Yine de, genellikle hız açısından kritik durumlarda kullanmaktan kaçınmanın yolları vardır. Ve alternatifi tam olarak görmüyorum. Bu SO yanıtı , türü depolamak için temel sınıfta tanımlanan bir enum kullanılmasını önerir. Bu yalnızca türetilmiş sınıflarınızı önceden biliyorsanız çalışır. Bu oldukça büyük bir "eğer"!
Bu yanıttan, RTTI'nin maliyeti de net değil gibi görünüyor. Farklı insanlar farklı şeyleri ölçer.
Zarif polimorfik tasarımlar RTTI'yi gereksiz kılacaktır
Bu ciddiye aldığım bir tavsiye. Bu durumda, RTTI kullanım durumumu kapsayan iyi RTTI olmayan çözümler bulamıyorum. Bir örnek vereyim:
Bazı tür nesnelerin grafiklerini işlemek için bir kütüphane yazdığımı varsayalım. Kullanıcıların kitaplığımı kullanırken kendi türlerini oluşturmalarına izin vermek istiyorum (bu nedenle enum yöntemi kullanılamaz). Düğümüm için bir temel sınıfım var:
class node_base
{
public:
node_base();
virtual ~node_base();
std::vector< std::shared_ptr<node_base> > get_adjacent_nodes();
};
Şimdi, düğümlerim farklı türlerde olabilir. Peki ya bunlar:
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);
};
Cehennem, neden bunlardan biri bile değil:
class orange_node : public red_node, public yellow_node
{
public:
orange_node();
virtual ~orange_node();
void poke();
void poke_adjacent_oranges();
};
Son işlev ilginç. İşte yazmanın bir yolu:
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();
}
}
}
Bunların hepsi açık ve temiz görünüyor. İhtiyacım olmayan öznitelikleri veya yöntemleri tanımlamama gerek yok, temel düğüm sınıfı zayıf ve anlamsız kalabilir. RTTI olmadan nereden başlamalıyım? Belki temel sınıfa bir node_type özniteliği ekleyebilirim:
class node_base
{
public:
node_base();
virtual ~node_base();
std::vector< std::shared_ptr<node_base> > get_adjacent_nodes();
private:
std::string my_type;
};
Std :: string bir tür için iyi bir fikir mi? Belki hayır, ama başka ne kullanabilirim? Bir numara oluşturun ve henüz kimsenin kullanmadığını umuyor musunuz? Ayrıca, orange_node'um durumunda, red_node ve yellow_node'daki yöntemleri kullanmak istersem ne olur? Düğüm başına birden çok tür depolamam gerekir mi? Bu karmaşık görünüyor.
Sonuç
Bu örnekler aşırı derecede karmaşık veya sıradışı görünmüyor (Düğümlerin yazılım aracılığıyla kontrol edilen ve ne olduklarına bağlı olarak çok farklı şeyler yapan gerçek donanımı temsil ettiği günlük işimde benzer bir şey üzerinde çalışıyorum). Yine de bunu şablonlarla veya diğer yöntemlerle yapmanın net bir yolunu bilmiyordum. Lütfen örneğimi savunmak yerine sorunu anlamaya çalıştığımı unutmayın. Yukarıda bağlantılandırdığım SO yanıtı ve Wikibooks'taki bu sayfa gibi sayfaları okumam, RTTI'yi kötüye kullandığımı gösteriyor gibi görünüyor, ancak nedenini öğrenmek istiyorum.
Öyleyse, orijinal soruma geri dönelim: Neden 'saf polimorfizm' RTTI kullanmaya tercih edilir?
node_base
bir kütüphanenin parçası olduğunu ve kullanıcıların kendi düğüm türlerini oluşturacağını not etmediğine dikkat ediyorum . O zaman başka bir çözüme izin vermek için değişiklik yapamazlarnode_base
, bu yüzden belki RTTI onların en iyi seçeneği olabilir. Öte yandan, böyle bir kitaplık tasarlamanın başka yolları da vardır, böylece yeni düğüm türleri RTTI kullanmaya gerek kalmadan (ve yeni düğüm türlerini tasarlamanın başka yollarını da) çok daha zarif bir şekilde sığabilir.