[英]How to use dynamic_cast to downcast correctly?
I am being very confused about dynamic_cast
.我对
dynamic_cast
感到非常困惑。 Material from C++ Primer and cppreference (rule 5) can't help me understand.来自C++ Primer和cppreference (规则 5)的材料无法帮助我理解。 (cppreference is way much harder than the book and I read them both very carefully)
(cppreference 比这本书难多了,我非常仔细地阅读了它们)
From C++ Primer 5th :来自C++ Primer 5th :
dynamic_cast<type*>(e)
In all cases, the type of
e
must be either a class type that is publicly derived from the target type, apublic
base class of the target type, or the same as the target type.在所有情况下,
e
的类型必须是从目标类型公开派生的类类型、目标类型的public
基类,或者与目标类型相同。 Ife
has one of these types, then the cast will succeed...如果
e
具有这些类型之一,则转换将成功...
So here's how I understand the quoted text above:所以这就是我如何理解上面引用的文字:
(Base class have virtual functions) (基类有虚函数)
dynamic_cast
succeeds if : dynamic_cast
成功,如果:
e
is a public inherited derived class from type
. e
是从type
公共继承的派生类。 e
is children. e
是儿童。 Upcast.e
is a base class of type
? e
是type
的基类? type
is children. type
是儿童。 Downcast.e
is same as type
. e
与type
相同。 Sidecast? Sample code:示例代码:
#include <iostream>
using namespace std;
struct A {
virtual void foo() {}
};
struct B : A {
};
struct C : B {
};
int main()
{
A* pa = new B;
if (C* pc = dynamic_cast<C*>(pa)) {
cout << "1"; //B is a base class of C
}
return 0;
}
I don't understand why this downcast would fail, I think it satisfies condition 2. and rule 5) (from cppreference).我不明白为什么这种向下转型会失败,我认为它满足条件 2.和规则 5) (来自 cppreference)。
If the book is wrong(damn once again), would someone elaborate rule 5) from cppreference?如果这本书是错误的(再次该死),有人会详细说明 cppreference 中的规则 5)吗? I can't fully understand what it say without examples...
没有例子我无法完全理解它所说的......
Here is the rule from cppreference with my annotations:这是 cppreference 中带有我的注释的规则:
5) If expression is a pointer or reference to a polymorphic type Base, and new_type is a pointer or reference to the type Derived a run-time check is performed:
5) 如果 expression 是指向多态类型 Base 的指针或引用,并且 new_type 是指向类型 Derived 的指针或引用,则执行运行时检查:
This applies.这适用。
B
is a base of C
. B
是C
的基数。
a) The most derived object pointed/identified by expression is examined.
a) 检查表达式指向/标识的最派生对象。 If, in that object, expression points/refers to a public base of Derived, and if only one subobject of Derived type is derived from the subobject pointed/identified by expression, then the result of the cast points/refers to that Derived subobject.
如果在该对象中,表达式指向/引用派生的公共基础,并且如果只有一个派生类型的子对象从表达式指向/标识的子对象派生,则转换的结果指向/引用该派生子对象。 (This is known as a "downcast".)
(这被称为“垂头丧气”。)
The most dervied object pointed by pa
is of type B
. pa
指向的最衍生的对象是B
类型。
Although B
is a public base of C
, the particular instance which pa
points to, is not an instance of B
base subobject of a C
instance.尽管
B
是C
的公共基础,但pa
指向的特定实例不是C
实例的B
基础子对象的实例。 The pointed B
instance is a "concrete" object.指向的
B
实例是一个“具体”对象。 So, this case does not apply.所以,本案不适用。
An example:一个例子:
C c;
B* bp = &c; // bp points to base subobject of C
C* cp = dynamic_cast<C*>(bp);
assert(cp);
B b2;
B* bp2 = &b2; // bp does not point to a base subobject
C* cp2 = dynamic_cast<C*>(bp2);
assert(!cp2);
b) Otherwise, if expression points/refers to a public base of the most derived object, and, simultaneously, the most derived object has an unambiguous public base class of type Derived, the result of the cast points/refers to that Derived (This is known as a "sidecast".)
b) 否则,如果表达式指向/引用最派生对象的公共基类,并且同时,最派生对象具有派生类型的明确公共基类,则转换的结果指向/引用那个派生对象(这被称为“侧播”。)
pa
does not point to a most derived object whose base class is C
, so this case does not apply. pa
不指向基类为C
的最派生对象,因此这种情况不适用。
An example of side cast:侧铸的一个例子:
struct base {
virtual ~base(){}; // for polymorphism
};
struct left : base {};
struct right : base {};
struct derived : left, right {};
derived d;
left* l = &d;
right* r = dynamic_cast<right*>(l);
c) Otherwise, the runtime check fails .
c)否则,运行时检查失败。 If the dynamic_cast is used on pointers, the null pointer value of type new_type is returned .
如果在指针上使用 dynamic_cast,则返回 new_type 类型的空指针值。 If it was used on references, the exception std::bad_cast is thrown.
如果它用于引用,则抛出异常 std::bad_cast。
Neither 5a nor 5b cases apply, so this "otherwise" case 5c does. 5a 和 5b 情况都不适用,所以这个“否则”情况 5c 不适用。
- e is same as type.
e 与类型相同。 Sidecast?
侧播?
Not a sidecast.不是侧倾。 A sidecast is explained in 5b.
侧播在 5b 中解释。 Casting to same type is just an identity cast (rarely useful, so not a commonly used terminology either).
转换为相同类型只是身份转换(很少有用,因此也不是常用术语)。
It may be that the conditions of the book attempt describe whether the conversion is well-formed.可能是本书尝试的条件描述了转换是否格式良好。 Although, "then the cast will succeed" certainly seems to imply more.
虽然, “然后演员会成功”当然似乎意味着更多。 The quoted rules are not correct for describing whether the cast succeeds at runtime.
引用的规则对于描述转换在运行时是否成功是不正确的。
If the entire program is well-formed, then a compiler must compile the program.如果整个程序格式良好,则编译器必须编译该程序。 If an expression is ill-formed, then a compiler must give you a diagnostic message saying that you did wrong.
如果表达式格式错误,那么编译器必须给您一条诊断消息,说明您做错了。
The example program that you've shown is well-formed and it must successfully compile.您展示的示例程序格式良好,必须成功编译。 It does compile on my system.
它确实在我的系统上编译。
The last part is that the dynamic type of the object must match.最后一部分是对象的动态类型必须匹配。
Here, you have a B
pointed to by an A
pointer.在这里,您有一个
A
指针指向的B
You are trying to dynamically cast the pointer to get a pointer to C
, but there is no C
to point to.您正在尝试动态转换指针以获取指向
C
的指针,但没有C
指向。 So the cast fails.所以演员表失败了。
dynamic cast doesn't create objects, it just lets you access objects that are already there.动态转换不创建对象,它只是让您访问已经存在的对象。 When you call
new B
it creates a B
object with an A
subobject.当您调用
new B
它会创建一个带有A
子对象的B
对象。 It does not create a C
object.它不创建
C
对象。
The issue is that the statement A* pa = new B;
问题在于语句
A* pa = new B;
creates a B
.创建一个
B
。
But a B
doesn't contain a C
(downwards, upwards, or sideways), so the dynamic cast from pa
to a C*
will certainly fail.但是
B
不包含C
(向下、向上或横向),因此从pa
到C*
的动态转换肯定会失败。
I'm, for the most part, adding another example of a side cast to previous answers…在大多数情况下,我在之前的答案中添加了另一个侧面演员的例子......
struct A {};
struct B { virtual ~B() = default; };
struct C : A, B {};
A *side_cast()
{
B *obj = new C;
return dynamic_cast<A *>(obj);
}
The above is a legal "side cast", and does not return null.以上是合法的“side cast”,不返回 null。 This shows that the target type neither :
这表明目标类型既不是:
*
expression .*
表达式的静态类型相关。 for the cast to succeed at run time .为演员在运行时成功。
The cast is well-formed , regardless of whether the type of the most-derived object pointed to by expression (IOW, the dynamic type of *
expression ) inherits from the target type.无论表达式指向的派生最多的对象的类型(IOW,
*
表达式的动态类型)是否继承自目标类型,强制转换都是格式良好的。 However, it will return static_cast<A *>(nullptr)
unless A
is a public and unambiguous base class of the dynamic type of *
expression .但是,它将返回
static_cast<A *>(nullptr)
除非A
是*
expression的动态类型的公共且明确的基类。 The gist of this is that you can legally write a whole bunch of nonsensical casts like dynamic_cast<std::tuple<int, float> *>(&std::cin)
– note that std::cin
is of type std::istream
which is polymorphic –, but you'll simply get a null pointer at runtime.其要点是您可以合法地编写一大堆无意义的强制转换,例如
dynamic_cast<std::tuple<int, float> *>(&std::cin)
– 请注意std::cin
的类型为std::istream
这是多态的——但你只会在运行时得到一个空指针。
Simply put, a dynamic_cast
between pointers can do most of the things that a static_cast
can (except, at least, non-polymorphic downcast*), and, when the static type of *
expression is polymorphic, it can also cast to any pointer to class whatsoever, except for removing cv-qualifers ( const
or volatile
).简单地说,指针之间的
dynamic_cast
可以做static_cast
可以做的大部分事情(至少,非多态向下转换*除外),并且,当*
表达式的静态类型是多态时,它也可以转换到任何指向类,除了删除cv 限定符( const
或volatile
)。 However, whether the cast actually returns non-null depends on the specific condition, checked at run time, mentioned above.但是,强制转换是否实际返回非 null 取决于特定条件,在运行时检查,如上所述。
* The reason why this is forbidden is that there is no safe way to do this, and dynamic_cast
is expected to be safe. * 之所以禁止这样做,是因为没有安全的方法可以做到这一点,预计
dynamic_cast
是安全的。 Hence they make you write static_cast
to make it clear that any ensuing UB is your fault.因此,他们让您编写
static_cast
以明确表示任何随后的 UB 都是您的错。
I would like to add my two cents on top of @eerorika 's fabulous answer, primarily focusing on the phrase of我想在@eerorika 的精彩回答之上加上我的两分钱,主要集中在
If only one object of Derived type is derived from the subobject pointed/identified by an expression, then....
如果只有一个派生类型的对象是从表达式指向/标识的子对象派生的,那么......
I think this is to prevent downcasting given the following condition:我认为这是为了防止出现以下情况的向下转型:
Base
|
Derived
/ \
| V
| Right
\ /
V V
MostDerived
In this case, there 2 objects of Derived in MostDerived.在这种情况下,MostDerived 中有 2 个 Derived 对象。 Upon casting a Base pointer, which points to a MostDerived instance, to a Derived pointer, it is impossible for the compiler to know which of the Derived object one's referring to.
在将指向 MostDerived 实例的 Base 指针转换为 Derived 指针时,编译器不可能知道它指的是哪个 Derived 对象。 (The one that is referred by MostDerived or the one that's referred by Right.)
(由 MostDerived 引用的那个或由 Right 引用的那个。)
In fact, this is the exact case of ambiguous base access and will cause compiler warning or failed compilation depends on your implementation.事实上,这正是基址访问不明确的情况,会导致编译器警告或编译失败,具体取决于您的实现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.