C++之dynamic_cast
前言
dynamic_cast运算符牵扯到的面向对象的多态性跟程序运行时的状态,所以不能完全的使用传统的转换方式来替代。因此是最常用,最不可缺少的一个运算符,与static_cast一样,dynamic_cast的转换也需要目标类型和源对象有一定的关系:继承关系。 更准确的说,dynamic_cast是用来检查两者是否有继承关系。因此该运算符实际上只接受基于类对象的指针和引用的类转换。从这个方面来看,似乎dynamic_cast又和reinterpret_cast是一致的,但实际上,它们还是存在着很大的差别。引用
如果转换成功, dynamic_cast
将返回一个new-type类型的值。如果转换失败,new-type是指针类型,则返回该类型的空指针
。如果转换失败并且new-type是引用类型,它将抛出一个与std::bad_cast类型的处理程序匹配的异常
。
dynamic_cast
dynamic_cast(expression)
Note:
只有以下转换可以使用dynamic_cast完成,除非这些转换会丢弃constness或volatile。
- 如果expression的类型恰好是new-type或new-type的一个较少cv(constness and volatile)限定版本,则结果是expression的值,类型为new-type。(换句话说,dynamic_cast可以用来添加constness或volatile。
隐式转换
和static_cast
也可以执行此转换)
D* d1 = new D();
D* d2 = dynamic_cast<D*>(d1);
d2->hello();
const D *d3 = dynamic_cast<const D *>(d1);
- 如果expression的值是
空指针
值,则结果是new-type的空指针值。
D *ptr = NULL;
D* ptr2 = dynamic_cast<D*>(ptr);
if(ptr2 == NULL){
printf("ptr2 is NULL\n");
}
控制台输出
2) ptr2 is NULL
- 如果new-type是一个指向
Base
的指针或引用,expression的类型是一个指向Derived
的指针或引用,其中Base
是Derived
的一个唯一的、可访问的基类,则结果是一个指向由expression指向或定义的Derived对象中的基类子对象的指针或引用。(注意:隐式转换和static_cast也可以执行此转换)
- 如果expression是指向
多态类型
的指针,而new-type是指向void
的指针,则结果是指向由expression指向或引用的派生最底层
的对象的指针。
5. 如果expression是指向多态类型
Base的指针或引用,而new-type是指向Derived的指针或引用,则执行运行时检查
,这应该是dynamic_cast使用最多的场景:
- a)如果在该对象中,expression是派生的
公共基
的指向/引用,并且如果只有一个派生类型的对象是从expression所指向/定义的子对象派生出来的,则强制转换的结果指向/引用该派生对象。(downcast”)
D d; // the most derived object
A &a = d; // upcast, dynamic_cast may be used, but unnecessary
[[maybe_unused]] D &new_d = dynamic_cast<D &>(a); // downcast
new_d.hello();
-
b)否则,如果expression是派生最底层的对象的
公共基类
的指针或引用,同时,派生最底层的对象有一个类型为derived的明确的公共基类,则转换的结果指向/引用了该derived。(sidecast)D d; // the most derived object A &a = d; // upcast, dynamic_cast may be used, but unnecessary [[maybe_unused]] B &new_b = dynamic_cast<B &>(a); // sidecast new_b.hello();
- c)否则,
运行时
检查失败。如果在指针上使用dynamic_cast,则返回new-type类型的空指针值。如果它用于引用,则抛出异常
std::bad_cast。
6. 当dynamic_cast在构造函数或析构函数中使用时(直接或间接),expression指向当前正在构造/销毁
的对象,则该对象被认为是派生最底层对象。如果new-type不是指向构造函数/析构函数自己的类或基类之一的指针或引用,则该行为未定义
, 如下例中的6)
类似于其他强制转换表达式,其结果为:
-
如果new-type是一个左值引用类型(表达式必须是一个左值),则结果是一个左值,
-
如果new-type是一个右值引用类型(表达式可以是左值,或者右值(直到c++ 17)必须是一个完整类类型的glvalue (从c++ 17开始)), 则结果是xvalue.
-
如果new-type是一个指针类型,则是一个prvalue.
几个value的定义,可以参考https://cloud.tencent.com/developer/article/1493839
示例:
struct V
{
virtual void f() {
} // must be polymorphic to use runtime-checked dynamic_cast
};
struct A : virtual V
{
void hello()
{
printf("hello, I am A\n");
}
};
struct B : virtual V
{
B(V *v, A *a)
{
// casts during construction (see the call in the constructor of D below)
dynamic_cast<B *>(v); // well-defined: v of type V*, V base of B, results in B*
dynamic_cast<B *>(a); // undefined behavior: a has type A*, A not a base of B
}
void hello()
{
printf("hello, I am B\n");
}
};
struct D : A, B
{
//6)
D() : B(static_cast<A *>(this), this) {
}
void hello(){
printf("hello, I am D\n");
}
};
struct Base
{
virtual ~Base() {
}
};
struct Derived : Base
{
virtual void name() {
}
};
void test_dynamic_cast(){
D d; // the most derived object
A &a = d; // upcast, dynamic_cast may be used, but unnecessary
[[maybe_unused]] D &new_d = dynamic_cast<D &>(a); // downcast
new_d.hello();
[[maybe_unused]] B &new_b = dynamic_cast<B &>(a); // sidecast
new_b.hello();
Base *b1 = new Base;
if (Derived *d = dynamic_cast<Derived *>(b1); d != nullptr)
{
std::cout << "downcast from b1 to d successful\n";
d->name(); // safe to call
}
Base *b2 = new Derived;
if (Derived *d = dynamic_cast<Derived *>(b2); d != nullptr)
{
std::cout << "downcast from b2 to d successful\n";
d->name(); // safe to call
}
delete b1;
delete b2;
}
控制台输出
sh-4.4$ ./build/linux/x86_64/release/Class-convert
hello, I am D
hello, I am B
downcast from b2 to d successful