专栏首页C++核心准则原文翻译C++核心准则C.146:如果无法避免在继承层次中移动,使用dynamic_cast

C++核心准则C.146:如果无法避免在继承层次中移动,使用dynamic_cast

C.146: Use dynamic_cast where class hierarchy navigation is unavoidable

C.146:如果无法避免在继承层次中移动,使用dynamic_cast

Reason(原因)

dynamic_cast is checked at run time.

dynamic_cast会在运行时检查。

Example(示例)
struct B {   // an interface
    virtual void f();
    virtual void g();
    virtual ~B();
};

struct D : B {   // a wider interface
    void f() override;
    virtual void h();
};

void user(B* pb)
{
    if (D* pd = dynamic_cast<D*>(pb)) {
        // ... use D's interface ...
    }
    else {
        // ... make do with B's interface ...
    }
}

Use of the other casts can violate type safety and cause the program to access a variable that is actually of type X to be accessed as if it were of an unrelated type Z:

使用其他的类型转换无法保证类型安全并导致程序将一个实际上是类型X的变量当成类型Z访问。

void user2(B* pb)   // bad
{
    D* pd = static_cast<D*>(pb);    // I know that pb really points to a D; trust me
    // ... use D's interface ...
}

void user3(B* pb)    // unsafe
{
    if (some_condition) {
        D* pd = static_cast<D*>(pb);   // I know that pb really points to a D; trust me
        // ... use D's interface ...
    }
    else {
        // ... make do with B's interface ...
    }
}

void f()
{
    B b;
    user(&b);   // OK
    user2(&b);  // bad error
    user3(&b);  // OK *if* the programmer got the some_condition check right
}
Note(注意)

Like other casts, dynamic_cast is overused. Prefer virtual functions to casting. Prefer static polymorphism to hierarchy navigation where it is possible (no run-time resolution necessary) and reasonably convenient.

向其他类型转换一样,dynamic_cast也被过度使用了。更应该使用虚函数而不是类型转换。在继承体系中移动时如果可能(不需要执行时决定)而且更便利的话应该利用静态多态机制。

Note(注意)

Some people use dynamic_cast where a typeid would have been more appropriate; dynamic_cast is a general "is kind of" operation for discovering the best interface to an object, whereas typeid is a "give me the exact type of this object" operation to discover the actual type of an object. The latter is an inherently simpler operation that ought to be faster. The latter (typeid) is easily hand-crafted if necessary (e.g., if working on a system where RTTI is -- for some reason -- prohibited), the former (dynamic_cast) is far harder to implement correctly in general.

有些人在typeid更合适的时候使用dynamic_cast; dyamic_cast只是一个为了发现对象的最优接口而使用的判断"是某种类型"的通常操作。而typeid是”告诉我对象的实际类型"的操作,用于得到对象的类型。后者一定会更简单,也应该是更快的操作。如果有必要的话,后者(typeid)更容易自己实现(例如,如果由于某种原因,工作的系统禁止使用RTTI),一般来讲,前者(dynamic_cast)的正确实现要困难得多。

Consider(考虑):

struct B {
    const char* name {"B"};
    // if pb1->id() == pb2->id() *pb1 is the same type as *pb2
    virtual const char* id() const { return name; }
    // ...
};

struct D : B {
    const char* name {"D"};
    const char* id() const override { return name; }
    // ...
};

void use()
{
    B* pb1 = new B;
    B* pb2 = new D;

    cout << pb1->id(); // "B"
    cout << pb2->id(); // "D"


    if (pb1->id() == "D") {         // looks innocent
        D* pd = static_cast<D*>(pb1);
        // ...
    }
    // ...
}

The result of pb2->id() == "D" is actually implementation defined. We added it to warn of the dangers of home-brew RTTI. This code may work as expected for years, just to fail on a new machine, new compiler, or a new linker that does not unify character literals.

pb2->id() == "D"的结果实际上是实现决定的。我们将它加进来是为了警示自制RTTI的危险性。这段代码可能会如愿工作很多年,只是到了一个没有统一字符字面量的新机器,新编译器或者新连接器时会失败。

If you implement your own RTTI, be careful.

如果以自己实现RTTI,要小心。

Exception(例外)

If your implementation provided a really slow dynamic_cast, you may have to use a workaround. However, all workarounds that cannot be statically resolved involve explicit casting (typically static_cast) and are error-prone. You will basically be crafting your own special-purpose dynamic_cast. So, first make sure that your dynamic_cast really is as slow as you think it is (there are a fair number of unsupported rumors about) and that your use of dynamic_cast is really performance critical.

如果你的实现提供了一个真的很慢的dynamic_cast,你也许必须变通。然而,所有的变通都无法静态解决而且容易发生错误,包括显示类型转换(通常是static_cast)。你只能设计出用于特殊目的的dynamic_cast。因此,首先确认你的dynamic_cast真的像你想的那么慢(关于这件事存在一些未经证实的谣言)并且你使用dynamic_cast的地方对性能是否真的那么敏感。

We are of the opinion that current implementations of dynamic_cast are unnecessarily slow. For example, under suitable conditions, it is possible to perform a dynamic_cast in fast constant time. However, compatibility makes changes difficult even if all agree that an effort to optimize is worthwhile.

我们认为目前dynamic_cast的实现有些不必要地慢了。例如,在合适的条件下,dynamic_cast可以在很短的固定时间内完成。然而,兼容性使变更很困难,即使所有人都同意优化的有价值的。

In very rare cases, if you have measured that the dynamic_cast overhead is material, you have other means to statically guarantee that a downcast will succeed (e.g., you are using CRTP carefully), and there is no virtual inheritance involved, consider tactically resorting static_cast with a prominent comment and disclaimer summarizing this paragraph and that human attention is needed under maintenance because the type system can't verify correctness. Even so, in our experience such "I know what I'm doing" situations are still a known bug source.

存在非常罕见的情况,如果你已经判定dynamic_cast的影响是确实存在的,你可以使用其他的方式静态保证向下转换会成功(例如,你小心地使用了CRTP),而且不涉及到虚继承的话,可以考虑战术上采用带有明显注释的static_cast。但是由于类型系统不能进行正确性验证,需要对这段代码进行免责声明,并且进行认为提醒。即使做到这种程度,在我们的经验中,像这样“我知道我在做什么"的情况仍然是一个有名的错误源。

Exception(例外)

Consider(考虑如下代码):

template<typename B>
class Dx : B {
    // ...
};
Enforcement(实施建议)
  • Flag all uses of static_cast for downcasts, including C-style casts that perform a static_cast.
  • 指出使用static_cast实现向下转换的情况,包括执行static_cast的C风格转换。
  • This rule is part of the type-safety profile.
  • 本规则也是类型安全规则群的内容

译者注:

RTTI:运行时类型信息。

CRTP:静态分发。具体请参照:

https://eli.thegreenplace.net/2013/12/05/the-cost-of-dynamic-virtual-calls-vs-static-crtp-dispatch-in-c/

原文连接:

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c146-use-dynamic_cast-where-class-hierarchy-navigation-is-unavoidable


觉得本文有帮助?请分享给更多人。

关注【面向对象思考】轻松学习每一天!

面向对象开发,面向对象思考!

本文分享自微信公众号 - 面向对象思考(OOThinkingDalian),作者:面向对象思考

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-02-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C++核心准则ES.49:如果必须进行类型转换,使用命名转换

    Readability. Error avoidance. Named casts are more specific than a C-style or fu...

    面向对象思考
  • C++核心准则ES.12:不要在嵌套作用域中重复使用同样的名称

    It is easy to get confused about which variable is used. Can cause maintenance p...

    面向对象思考
  • C++核心准则C.148:使用dynamic_cast进行指针类型转换时,将不能发现目标类看作是有效的选项

    C.148:使用dynamic_cast进行指针类型转换时,将不能发现目标类看作是有效的选项

    面向对象思考
  • C++中的显式类型转化

      类型转化也许大家并不陌生,int i; float j; j = (float)i; i = (int)j; 像这样的显式转化其实很常见,强制类型转换可能会...

    弗兰克的猫
  • 一篇文章牢记C/C++指针和引用区别

    海盗船长
  • Head First Python (一

    py3study
  • xue微xue微深入地聊一聊PHP session

    大家好,我今天打算换一个新的出场方式。所以,我打算从下面倒计时后开始重新打招呼,你们就假装开头这句话我没写配合一下,谢谢。

    老李秀
  • xpath路径表达式笔记

    xpath可以用来选择这7种节点。不过,下面的笔记只涉及最常用的第一种element(元素节点),因此可以将下文中的节点和元素视为同义词。

    ruanyf
  • 精心整理了15道面试官喜欢问的MyBatis面试题

    答:MyBatis 的缓存分为一级缓存和二级缓存,一级缓存放在 session 里面,默认就有,二级缓

    程序员追风
  • static_cast ,reinterpret_cast

    用法:static_cast < type-id > ( expression ) 该运算符把expression转换为type-id类型,但没有运行时类型检查...

    猿人谷

扫码关注云+社区

领取腾讯云代金券