几乎我见过的每一个讨论这类问题的 C + + 资源都告诉我,相对于使用 RTTI (运行时类型识别) ,我更喜欢多态方法。一般来说,我会认真对待这种建议,并且会试着理解它的基本原理——毕竟,C + + 是一个强大的怪兽,很难全面理解。然而,对于这个特殊的问题,我一无所知,想看看互联网能提供什么样的建议。首先,让我总结一下到目前为止我所学到的东西,列出为什么 RTTI 被认为是有害的常见原因:
我真的不相信这个论点。这就像是说我不应该使用 C + + 14特性,因为有些编译器不支持它。然而,没有人会阻止我使用 C + + 14特性。大多数项目都会对他们使用的编译器以及它的配置方式产生影响。甚至引用海湾合作委员会的页面:
-fno-rtti
使用虚函数禁用生成关于每个类的信息,以供 C + + 运行时类型标识特性使用 (Dynamic _ cast 和 typeid)。如果您不使用语言的这些部分,您可以使用这个标志来节省一些空间。注意这个例外 处理使用相同的信息,但是 G + + 根据需要生成它。Dynamic _ cast 操作符仍然可以用于不需要的强制转换 执行期型态讯息,即强制转换为“ void *”或明确的基类。
这告诉我的是,如果我没有使用 RTTI,我可以禁用它。这就像是说,如果你不使用 Boost,你不必链接到它。我没有计划的情况下,有人正在编译与 -fno-rtti
。另外,在这种情况下,编译器会明显失败。
每当我想使用 RTTI 时,这意味着我需要访问类的某种类型信息或 trait。如果我实现了一个不使用 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;
};
字符串对于类型来说是一个好主意吗?也许不是,但我还能用什么?编一个号码,希望没人用?另外,在我的 Orange _ node 的情况下,如果我想使用 red _ node 和 Yellow _ node 的方法,该怎么办?每个节点需要存储多个类型吗?听起来很复杂。
这个例子看起来并不过于复杂或不寻常(我在日常工作中也在做类似的事情,其中节点代表通过软件控制的实际硬件,并且根据它们是什么而做非常不同的事情)。然而,我不知道如何使用模板或其他方法来完成这项工作。 请注意,我试图理解这个问题,而不是为我的例子辩护。我阅读的页面,如 SO 答案,我链接上面和 维基百科上的这个页面似乎表明我滥用 RTTI,但我想知道为什么。
那么,回到我最初的问题: 为什么“纯多态性”优于使用 RTTI?