如果 dynamic_cast<void*> 强制转换一个底层非最派生类的对象会发生什么?

问题描述 投票:0回答:2

我们知道

dynamic_cast<void*>
会强制转换一个指向最派生对象的指针;但是如果底层对象不是最派生的呢?例如:

class BaseClass { public: virtual void dummy() { std::cout << "Base\n"; } };

class DerivedClass : public BaseClass {
    int a{}; 
public:
    void dummy() { std::cout << "Derived\n"; } 
};
class MostDerivedClass : public DerivedClass {
    int b{}; 
public:
    void dummy() { std::cout << "Most\n"; } 
};

BaseClass* basePtr_d = new DerivedClass, *basePtr_md = new MostDerivedClass;
DerivedClass* derivedPtr = 
    dynamic_cast<DerivedClass*>(basePtr_d); // right
MostDerivedClass* mostDerivedPtr = 
    dynamic_cast<MostDerivedClass*>(basePtr_md); // right
MostDerivedClass* mostDerivedPtr2 = 
    static_cast<MostDerivedClass*>(dynamic_cast<void*>(basePtr_md)); // right
DerivedClass* derivedPtr2 = 
    static_cast<DerivedClass*>(dynamic_cast<void*>(basePtr_d)); // What happens??

最后一个案例会怎样?

c++ derived-class dynamic-cast
2个回答
1
投票
DerivedClass* derivedPtr2 = 
    static_cast<DerivedClass*>(dynamic_cast<void*>(basePtr_d)); // What happens??

这相当于 (1) 向下转换(动态),然后是 (2) 向上转换(静态),后者是定义明确的非问题:

void*             p1 = dynamic_cast<void*>(basePtr_d); // 1
MostDerivedClass* p2 = reinterpret_cast<MostDerivedClass*>(p1); // †
DerivedClass*     p3 = static_cast<DerivedClass*>(p2);  // 2

很容易忽略重要的区别:指针类型和指针值,后者有时被称为指针的动态类型。

B
派生自
A
时,类型
A*
的指针和类型
B*
的指针都指向同一个类型
B
的对象可能具有不同的 values。对
dynamic_cast<void*>
的限制涵盖了这种特殊性,并保证了最派生类型的返回值,即使返回指针的静态类型当然是
void*
.

__

† 为了使等效性起作用,需要进行显式指针转换。这是安全的,因为在

dynamic_cast<new-type>(expression)
中,如果 expression 是指向多态类型的指针,而 new-type 是指向 void 的指针,则结果是指向由 expression 指向或引用的最派生对象的指针。见
dynamic_cast #4


0
投票

static_cast
中的所有
void*
所做的就是更改类型而不更改指针的值。它与
reinterpret_cast
相同。 在这种情况下,一切都应该没问题,因为
MostDerivedClass
对象与其
DerivedClass
子对象具有相同的地址。

如果不是这种情况,

derivedPtr2
将是一个无效指针。从技术上讲,这可能仍然是 UB,但我不确定。 说你的
MostDerivedClass
实际上是这样的:

struct X { int i; };

class MostDerivedClass : public X, public DerivedClass {
    int b{}; 
public:
    void dummy() { std::cout << "Most\n"; } 
};

现在做一个

static_cast
MostDerivedClass*
DerivedClass*
改变指针的值,可能通过添加四个字节的偏移量。 现在这条线

DerivedClass* derivedPtr2 = 
    static_cast<DerivedClass*>(dynamic_cast<void*>(basePtr_d)); // What happens??

静态地将一个空指针(指向

MostDerivedClass
对象或
X
子对象)投射到
DerivedClass*
,这本质上是一个
reinterpret_cast
。所以你最终得到一个
DerivedClass
类型的指针,它实际上指向
X
-子对象,而不是
DerivedClass
子对象。 所以你最终得到的
derivedPtr2
指针是无效的。

© www.soinside.com 2019 - 2024. All rights reserved.