dynamic_cast in C++
foreword
The dynamic_cast operator involves object-oriented polymorphism and the state of the program at runtime, so it cannot be completely replaced by traditional conversion methods. Therefore, it is the most commonly used and indispensable operator. Like static_cast , the conversion of dynamic_cast also requires a certain relationship between the target type and the source object: an inheritance relationship. More precisely, dynamic_cast is used to check whether there is an inheritance relationship between the two. So the operator actually only accepts class conversions based on pointers and references to class objects. From this point of view, it seems that dynamic_cast is consistent with reinterpret_cast , but in fact, there are still big differences between them. quote
If the conversion is successful, dynamic_cast
a new-type value will be returned. If the conversion fails and new-type is a pointer type, return that type 空指针
. If the conversion fails and the new-type is a reference type, it will throw a handler that matches the std::bad_cast type 异常
.
dynamic_cast
dynamic_cast(expression)
Note:
Only the following conversions can be done using dynamic_cast, unless those conversions discard constness or volatile.
- If the type of expression happens to be new-type or a less cv (constness and volatile) qualified version of new-type, the result is the value of expression, of type new-type. (In other words, dynamic_cast can be used to add constness or volatile.
隐式转换
Andstatic_cast
can also perform this conversion)
D* d1 = new D();
D* d2 = dynamic_cast<D*>(d1);
d2->hello();
const D *d3 = dynamic_cast<const D *>(d1);
- If the value of expression is
空指针
a value, the result is a new-type null pointer value.
D *ptr = NULL;
D* ptr2 = dynamic_cast<D*>(ptr);
if(ptr2 == NULL){
printf("ptr2 is NULL\n");
}
console output
2) ptr2 is NULL
- If new-type is a pointer
Base
or reference to and the type of expression is a pointer orDerived
reference to a unique, accessible base class of , then the result is a pointer to the Derived object pointed to or defined by expression A pointer or reference to the base class subobject of . (Note: implicit conversion and static_cast can also perform this conversion)Base
Derived
- If expression is a pointer to
多态类型
and new-type is a pointervoid
to, the result is a pointer to the derived最底层
object pointed to or referenced by expression.
5. If expression is 多态类型
a pointer or reference to Base, and new-type is a pointer or reference to Derived, then execute 运行时检查
. This should be the most used scenario of dynamic_cast:
- a) If, in that object, expression is a pointer
公共基
/reference to derived, and if there is only one object of derived type derived from the subobject pointed to/defined by expression, the result of the cast points/references to that derived object . (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) Otherwise, if expression is a pointer or reference to the bottom-most derived object, and at the same time, the bottom-most derived object has a definite common base class of type derived, then the result of the conversion points to/ references 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) Otherwise,
运行时
the check fails. If dynamic_cast is used on a pointer, a null pointer value of type new-type is returned.异常
If it is used for references, std::bad_cast is thrown .
6. When dynamic_cast is used in the constructor or destructor (directly or indirectly), the expression points to the current 构造/销毁
object, and the object is considered to be the bottom-level derived object. The behavior if the new-type is not a pointer or reference to the constructor/destructor's own class or one of the base classes, 未定义
6) in the example below
Similar to other cast expressions, the result is:
-
If new-type is an lvalue reference type (expression must be an lvalue), the result is an lvalue,
-
If new-type is an rvalue reference type (expression can be an lvalue, or an rvalue (until c++17) must be a glvalue of a complete class type (since c++17)), then the result is an xvalue.
-
If new-type is a pointer type, it is a prvalue.
For the definition of several values, please refer to https://cloud.tencent.com/developer/article/1493839
Example:
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;
}
console output
sh-4.4$ ./build/linux/x86_64/release/Class-convert
hello, I am D
hello, I am B
downcast from b2 to d successful