在下面的代码中,在案例1中构造obj
时,我们也构造了一个derived
类对象,但它的成员函数对于obj
是不可访问的。因此,在向下转换(即在案例2中)时,使用obj
作为源,我们已经构建了derived
。为什么obj
需要多态?
如果我把我与上面的描述混淆了,为什么{$ 1}}在向上转换时不需要是多态的,但在向下转换时 在使用{{1}时需要是多态的}?
obj
答案 0 :(得分:18)
为了使 dynamic_cast 工作,对象需要是多态的。这样做的原因是 dynamic_cast 需要某处存储将用于执行强制转换的类型信息,并且它通过将信息存储在类的vtable旁边来实现。为了有一个vtable,你需要至少有一个虚拟方法。
最简单的方法是将基类析构函数标记为虚拟。
Upcasting(即派生到base)不需要强制转换,因为编译器能够检查强制转换是否可以在编译时工作。然而,在向下转型时也是如此。
答案 1 :(得分:6)
来自5.2.7 / 1 [expr.dynamic.cast]:
表达式
dynamic_cast<T>(v)
的结果是将表达式v转换为type的结果 吨。[...]
如果T是“指向 cv1 B的指针”并且v具有类型“指向 cv2 D的指针”,那么B是D的基类,结果是 指向v。
指向的D对象的唯一B子对象的指针[...]
否则,v应为多态类型的指针或左值。
该标准甚至提供了以下示例,该示例说明多态类型要求不代表派生到基本转换:
struct B {};
struct D : B {};
void foo(D* dp)
{
B* bp = dynamic_cast<B*>(dp); // equivalent to B* bp = dp;
}
答案 2 :(得分:0)
的dynamic_cast
您必须详细了解Dynamic_cast(示例)there。
答案 3 :(得分:0)
class car
{
public:
virtual void drive()
{
std::cout <<"car"<<std::endl;
}
};
class toyota: public car
{
public:
virtual void drive()
{
std::cout <<"toyota"<<std::endl;
}
};
class honda: public car
{
public:
virtual void drive()
{
std::cout <<"honda"<<std::endl;
}
};
template <typename Tderived>
Tderived* dynamicCast(void* pBase)
{
//compare the vptr of the class pointed by pBase with a temporary Tderived class.
//If vptr of pBase and vptr of Tderived() are pointing to the same vtable
//then it can be safely deduced that pBase is indeed pointing to an instance of Tderived
if (*(int**)pBase == *(int**)&Tderived())
{
return (Tderived*)pBase;
}
else
{
return nullptr;
}
}
int main()
{
car* pCar;
honda hondaCar;
toyota toyotaCar;
pCar = &toyotaCar;
honda* pHonda = dynamicCast<honda>(pCar);
if (nullptr != pHonda)
{
pHonda->drive();
}
else
{
toyota* pToyota = dynamicCast<toyota>(pCar);
if (nullptr != pToyota)
{
pToyota->drive();
}
}
}
在上面的例子中,大多数编译器会通过检查b的vtable指针是否指向派生类D的vtable来实现动态强制转换。 如果是,它只返回b的地址作为返回值,否则返回nullptr。 这是动态演员执行时幕后可能发生的事情: -
{{1}}
现在,如果该类不是多态的,那么编译器无法找到pCar是指向本田还是丰田汽车。请注意,这只是实现dynamic_cast的方法之一,因为C ++标准没有谈论任何关于vtable的内容。