一些编译器不使用它 / RTTI并非总是启用
我真的不认同这个论点。这就像说我不应该使用C ++14功能,因为存在不支持它的编译器。然而,没有人会劝阻我使用C ++14功能。大多数项目将对他们正在使用的编译器及其配置产生影响。即使引用gcc手册:
-fno-rtti
禁用生成关于每个具有虚函数的类的信息,以供C++运行时类型识别功能(dynamic_cast和typeid)使用。如果您不使用语言的这些部分,则可以通过使用此标志节省一些空间。请注意,异常处理使用相同的信息,但G ++根据需要生成它。动态转换运算符仍然可以用于不需要运行时类型信息的转换,即转换为“void *”或非歧义基类。
这告诉我,如果我没有使用RTTI,我可以禁用它。这就像说,如果您没有使用Boost,则不必链接到它。我不必考虑某人正在使用-fno-rtti
进行编译的情况。此外,编译器在这种情况下会明确失败。
它会消耗额外的内存/可能会变慢
每当我想使用RTTI时,这意味着我需要访问类的某种类型信息或特征。如果我实现一个不使用RTTI的解决方案,通常意味着我将不得不向我的类添加一些字段来存储这些信息,所以内存参数有点无效(我将在下面举例说明)。
dynamic_cast可能会很慢。但是通常有方法可以避免在速度关键的情况下使用它。而且我并没有看到其他替代方法。 这个SO答案 建议在基类中定义一个枚举来存储类型。这仅适用于您预先知道所有派生类的情况。那是一个相当大的“如果”!
从那个答案中,似乎RTTI的成本也不清楚。不同的人测量不同的东西。
优雅的多态设计将使RTTI变得不必要
这是我认真考虑的建议。在这种情况下,我无法想出良好的非RTTI解决方案来覆盖我的RTTI使用情况。让我举个例子:
假设我正在编写一个处理某种对象图形的库。我希望允许用户在使用我的库时生成他们自己的类型(因此枚举方法不可用)。我有一个基类来表示节点:
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答案和this page on Wikibooks等页面,似乎表明我误用了RTTI,但我想学习原因。
所以,回到我的最初问题:为什么“纯粹的多态性”比使用RTTI更可取?
node_base
是库的一部分,用户将会创建他们自己的节点类型。那么他们无法修改node_base
来允许另一个解决方案,所以也许运行时类型信息(RTTI)就成为他们最好的选择。另一方面,有其他方法来设计这样一个库,使得新的节点类型可以更加优雅地适应其中,而不需要使用 RTTI(也有其他设计新节点类型的方式)。 - Matthew Walton