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
觉得本文有帮助?欢迎点赞并分享给更多的人。
阅读更多更新文章,请关注微信公众号【面向对象思考】