我所见过的几乎所有讨论这种事情的C ++资源都告诉我,与使用RTTI(运行时类型标识)相比,我应该更喜欢多态方法。总的来说,我会认真对待这种建议,并会尝试并理解其基本原理-毕竟,C ++是一头强大的野兽,很难全面理解。但是,对于这个特定的问题,我正在空白,想看看互联网可以提供什么样的建议。首先,让我总结一下到目前为止我所学到的东西,列出引起RTTI被“认为有害”的常见原因:
一些编译器不使用它/ RTTI并非始终启用
我真的不赞成这种说法。这就像说我不应该使用C ++ 14功能,因为那里有不支持它的编译器。但是,没有人会阻止我使用C ++ 14功能。大多数项目将对他们正在使用的编译器及其配置方式产生影响。甚至引用了gcc手册页:
-fno-rtti
使用C ++运行时类型标识功能(dynamic_cast和typeid)禁止使用虚拟函数生成有关每个类的信息。如果您不使用语言的这些部分,则可以使用此标志节省一些空间。请注意,异常处理使用相同的信息,但是G ++会根据需要生成它。dynamic_cast运算符仍可用于不需要运行时类型信息的强制转换,即强制转换为“ void *”或明确的基类。
这说明如果我不使用RTTI,可以将其禁用。这就像在说,如果您不使用Boost,则不必链接到它。我不需要为有人正在编译的情况做任何计划-fno-rtti
。另外,在这种情况下,编译器将大声失败。
花费额外的内存/可能很慢
每当我想使用RTTI时,这意味着我需要访问类的某种类型信息或特征。如果我实现了不使用RTTI的解决方案,这通常意味着我将不得不在类中添加一些字段来存储此信息,因此memory参数实在是无效的(我会在下面进一步举例说明)。
实际上,dynamic_cast可能很慢。不过,通常有避免使用速度要求严格的情况的方法。而且我还没有看到替代方案。这样的SO答案建议使用基类中定义的枚举来存储类型。这只有在您知道所有派生类都是先验的情况下才有效。相当大的“如果”!
从这个答案来看,RTTI的成本似乎也不清楚。不同的人测量不同的东西。
优雅的多态设计将使RTTI不再必要
这是我认真对待的建议。在这种情况下,我简直无法提出涵盖我的RTTI用例的良好的非RTTI解决方案。让我举一个例子:
假设我正在编写一个库来处理某种对象的图形。我想允许用户在使用我的库时生成自己的类型(因此enum方法不可用)。我的节点有一个基类:
class node_base
{
public:
node_base();
virtual ~node_base();
std::vector< std::shared_ptr<node_base> > get_adjacent_nodes();
};
现在,我的节点可以是不同的类型。这些怎么样:
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);
};
地狱,为什么不选择其中之一:
class orange_node : public red_node, public yellow_node
{
public:
orange_node();
virtual ~orange_node();
void poke();
void poke_adjacent_oranges();
};
最后一个功能很有趣。这是一种编写方法:
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();
}
}
}
这一切看起来都很清晰。我不必在不需要它们的地方定义属性或方法,基节点类可以保持精简和有意义。没有RTTI,我应该从哪里开始?也许我可以在基类中添加一个node_type属性:
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是类型的好主意吗?也许不是,但是我还能使用什么呢?编一个号码,希望没有人在使用它吗?另外,对于我的orange_node,如果我想使用red_node和yellow_node中的方法怎么办?每个节点是否必须存储多种类型?这看起来很复杂。
结论
这个示例似乎并不太复杂或异常(我在日常工作中使用的是类似的东西,其中节点表示通过软件控制的实际硬件,并且根据它们的不同而做不同的事情)。但是我不知道使用模板或其他方法来执行此操作的干净方法。请注意,我试图理解问题,而不是为我的例子辩护。我阅读了上面链接的SO答案等页面以及Wikibooks上的该页面似乎表明我滥用了RTTI,但我想了解原因。
那么,回到我最初的问题:为什么“纯多态性”优于使用RTTI?
node_base
是库的一部分,用户将创建自己的节点类型。然后他们将无法修改node_base
以允许其他解决方案,因此RTTI可能会成为他们的最佳选择。另一方面,还有其他方法可以设计这样的库,从而无需使用RTTI即可使新节点类型更加优雅地适应(也可以使用其他方式来设计新节点类型)。