为什么“纯多态性”优于使用RTTI? [英] Why is 'pure polymorphism' preferable over using RTTI?

查看:103
本文介绍了为什么“纯多态性”优于使用RTTI?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

几乎我所见过的讨论这种事情的每个C ++资源都告诉我,我应该使用多态方法来使用RTTI(运行时类型识别)。一般来说,我认真地采纳这种建议,并将尝试和理解的理由 - 毕竟,C ++是一个强大的野兽,很难了解其全面深入。然而,对于这个特定的问题,我正在绘制一个空白,并希望看到什么样的建议,互联网可以提供。首先,让我总结一下我到目前为止所学到的东西,列出引用为什么RTTI被认为有害的常见原因:

Almost every C++ resource I've seen that discusses this kind of thing tells me that I should prefer polymorphic approaches to using RTTI (run-time type identification). In general, I take this kind of advice seriously, and will try and understand the rationale -- after all, C++ is a mighty beast and hard to understand in its full depth. However, for this particular question, I'm drawing a blank and would like to see what kind of advice the internet can offer. First, let me summarize what I've learned so far, by listing the common reasons that are quoted why RTTI is "considered harmful":

我真的不买这个参数。这就像说我不应该使用C ++ 14的功能,因为有编译器,不支持它。然而,没有人会阻止我使用C ++ 14的功能。大多数项目将影响他们使用的编译器,以及它的配置。甚至引用gcc手册页:

I really don't buy this argument. It's like saying I shouldn't use C++14 features, because there are compilers out there that don't support it. And yet, no one would discourage me from using C++14 features. The majority of projects will have influence over the compiler they're using, and how it's configured. Even quoting the gcc manpage:


-fno-rtti

禁止使用虚拟函数生成每个类的信息,以供C ++运行时类型标识特性
(dynamic_cast和typeid)使用。如果不使用语言的这些部分,可以使用此标志来节省一些空间。注意,异常
处理使用相同的信息,但G ++根据需要生成它。 dynamic_cast运算符仍然可以用于不需要
运行时类型信息的转换,即转换为void *或转换为无歧义的基类。

Disable generation of information about every class with virtual functions for use by the C++ run-time type identification features (dynamic_cast and typeid). If you don't use those parts of the language, you can save some space by using this flag. Note that exception handling uses the same information, but G++ generates it as needed. The dynamic_cast operator can still be used for casts that do not require run-time type information, i.e. casts to "void *" or to unambiguous base classes.

这告诉我的是如果我没有使用RTTI,我可以禁用它。这就像说,如果你不使用Boost,你不必链接到它。我不必计划有人用 -fno-rtti 编译的情况。此外,在这种情况下,编译器会失败响亮和清晰。

What this tells me is that if I'm not using RTTI, I can disable it. That's like saying, if you're not using Boost, you don't have to link to it. I don't have to plan for the case where someone is compiling with -fno-rtti. Plus, the compiler will fail loud and clear in this case.

每当我试图使用RTTI,这意味着我需要访问某种类型的信息或我的类的特质。如果我实现一个不使用RTTI的解决方案,这通常意味着我将添加一些字段到我的类来存储这些信息,所以内存参数是一种无效(我会给一个例子进一步)。

Whenever I'm tempted to use RTTI, that means I need to access some kind of type information or trait of my class. If I implement a solution that does not use RTTI, this usually means I will have to add some fields to my classes to store this information, so the memory argument is kind of void (I'll give an example of this further down).

dynamic_cast可能很慢,确实如此。通常有办法避免使用速度关键的情况。我不太明白这个选择。 此SO回答建议使用在基类中定义的枚举来存储类型。这只有工作,如果你知道所有派生类a-priori。这是一个很大的如果!

A dynamic_cast can be slow, indeed. There's usually ways to avoid having to use it speed-critical situations, though. And I don't quite see the alternative. This SO answer suggests using an enum, defined in the base class, to store the type. That only works if you know all your derived classes a-priori. That's quite a big "if"!

从这个答案,似乎还看到RTTI的成本不清楚。不同的人测量不同的东西。

From that answer, it seems also that the cost of RTTI is not clear, either. Different people measure different stuff.

这是一种建议认真点。在这种情况下,我根本不能提出涵盖我的RTTI用例的良好的非RTTI解决方案。让我举一个例子:

This is the kind of advice I take seriously. In this case, I simply can't come up with good non-RTTI solutions that cover my RTTI use case. Let me provide an example:

说我在写一个库来处理某种对象的图形。我想允许用户在使用我的库时生成自己的类型(所以枚举方法不可用)。我有一个基类为我的节点:

Say I'm writing a library to handle graphs of some kind of objects. I want to allow users to generate their own types when using my library (so the enum method is not available). I have a base class for my node:

class node_base
{
  public:
    node_base();
    virtual ~node_base();

    std::vector< std::shared_ptr<node_base> > get_adjacent_nodes();
};

现在,我的节点可以是不同类型的。如何:

Now, my nodes can be of different types. How about these:

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);
};

地狱,为什么不是其中之一:

Hell, why not even one of these:

class orange_node : public red_node, public yellow_node
{
  public:
    orange_node();
    virtual ~orange_node();

    void poke();
    void poke_adjacent_oranges();
};

最后一个函数很有趣。这里有一种写法:

The last function is interesting. Here's a way to write it:

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属性到基类:

This all seems clear and clean. I don't have to define attributes or methods where I don't need them, the base node class can stay lean and mean. Without RTTI, where do I start? Maybe I can add a node_type attribute to the base class:

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的方法?我必须存储多个类型每个节点?这似乎很复杂。

Is std::string a good idea for a type? Maybe not, but what else can I use? Make up a number and hope no one else is using it yet? Also, in the case of my orange_node, what if I want to use the methods from red_node and yellow_node? Would I have to store multiple types per node? That seems complicated.

这个例子似乎不太复杂或不常见在我的工作类似的东西,其中节点表示通过软件控制的实际硬件,它们做非常不同的事情,取决于它们是什么)。然而我不知道一个干净的方式这样做与模板或其他方法。
请注意,我试图理解的问题,不保卫我的例子。我的阅读页面,如SO回答我链接上面和 Wikibook上的此页似乎建议我滥用RTTI,但我想了解为什么。

This examples doesn't seem overly complex or unusual (I'm working on something similar in my day job, where the nodes represent actual hardware that gets controlled through the software, and which do very different thing depending on what they are). Yet I wouldn't know a clean way of doing this with templates or other methods. Please note that I'm trying to understand the problem, not defend my example. My reading of pages such as the SO answer I linked above and this page on Wikibooks seem to suggest I'm misusing RTTI, but I would like to learn why.

因此,回到我原来的问题:为什么是纯多态性 RTTI?

So, back to my original question: Why is 'pure polymorphism' preferable over using RTTI?

推荐答案

接口描述了为了在代码中的给定情况下进行交互需要知道什么。一旦你用你的整个类型层次结构扩展接口,你的接口表面面积变得巨大,这使得推理更难。

An interface describes what one needs to know in order to interact in a given situation in code. Once you extend the interface with "your entire type hierarchy", your interface "surface area" becomes huge, which makes reasoning about it harder.

作为一个例子,你的相邻的橘子意味着我,作为第三方,不能效仿是一个橙色!你私下声明一个橙色类型,然后使用RTTI使你的代码在与该类型交互时表现特别。如果我想变成橙色,我必须在您的私人花园内。

As an example, your "poke adjacent oranges" means that I, as a 3rd party, cannot emulate being an orange! You privately declared an orange type, then use RTTI to make your code behave special when interacting with that type. If I want to "be orange", I must be within your private garden.

现在每个与orangeness整个橙色类型,并隐含地与您的整个私人花园,而不是一个定义的接口。

Now everyone who couples with "orangeness" couples with your entire orange type, and implicitly with your entire private garden, instead of with a defined interface.

虽然乍一看,这看起来像一个伟大的方式来扩展有限的接口,必须更改所有客户端(添加 am_I_orange ),趋向于发生的是代理基础,并阻止进一步扩展。特殊的变化对于系统的功能是固有的,并且阻止你创建一个橘子替换桔子,以不同的方式实现,可能会删除依赖或优雅地解决一些其他问题。

While at first glance this looks like a great way to extend the limited interface without having to change all clients (adding am_I_orange), what tends to happen instead is it ossifies the code base, and prevents further extension. The special orangeness becomes inherent to the functioning of the system, and prevents you from creating a "tangerine" replacement for orange that is implemented differently and maybe removes a dependency or solves some other problem elegantly.

这意味着你的界面必须足以解决你的问题。从那个角度来看,为什么你只需要戳橙子,如果是这样的话,为什么在界面中没有orangeness?如果你需要一些模糊的标签,可以添加ad-hoc,你可以添加到你的类型:

This does mean your interface has to be sufficient to solve your problem. From that perspective, why do you need to only poke oranges, and if so why was orangeness unavailable in the interface? If you need some fuzzy set of tags that can be added ad-hoc, you could add that to your type:

class node_base {
  public:
    bool has_tag(tag_name);

这提供了一个类似的大规模扩展您的接口从窄指定到广泛的标签。除了通过RTTI和实现细节(又称你是如何实现的?使用橙色类型,Ok你传递)之外,它通过一个完全不同的实现来轻松地模拟。

This provides a similar massive broadening of your interface from narrowly specified to broad tag-based. Except instead of doing it through RTTI and implementation details (aka, "how are you implemented? With the orange type? Ok you pass."), it does so with something easily emulated through a completely different implementation.

这甚至可以扩展到动态方法,如果你需要。 你支持Baz,Tom和Alice吗?好吧,Fooing你。在很大的意义上,这是一个动态的转换,比其他对象是一个你知道的类型。

This can even be extended to dynamic methods, if you need that. "Do you support being Foo'd with arguments Baz, Tom and Alice? Ok, Fooing you." In a big sense, this is less intrusive than a dynamic cast to get at the fact the other object is a type you know.

它仍然可以导致一个巨大的混乱,但它是至少一个混乱的消息和数据,而不是实现层次。

It can still lead to a huge mess, but it is at least a mess of messages and data, not implementation hierarchies.

抽象是一种解耦和隐藏不相关的游戏。它使代码更容易在本地进行推理。 RTTI正在钻孔直接通过抽象到实现细节。这可以使解决问题更容易,但它有成本锁定你一个具体的实现很容易。

Abstraction is a game of decoupling and hiding irrelevancies. It makes code easier to reason about locally. RTTI is boring a hole straight through the abstraction into implementation details. This can make solving a problem easier, but it has the cost of locking you into one specific implementation really easily.

这篇关于为什么“纯多态性”优于使用RTTI?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆