12

在下面的代码中,obj在案例 1 中构造的同时,我们也构造了一个derived类对象,但是它的成员函数是无法访问的obj。因此,在向下转换时(即,在情况 2 中),使用obj作为源,我们已经在其中构建derived了。为什么obj需要是多态的?

如果我对上面的描述感到困惑,为什么obj在向上转换时不需要是多态的,但是在向下转换时它在使用时确实需要是多态的dynamic_cast

class base
{
public:
    base()
    {
        cout<< " \n base constructor \n";
    }
};

class derived : public base
{
public:
    derived()
    {
        cout << " \n derived constructor \n";
    }
};

base *obj = dynamic_cast<base*> (new derived); // case 1: explicitly upcasting
derived *OBJ = dynamic_cast<derived*> (obj);   // case 2: error
4

4 回答 4

22

为了使dynamic_cast工作,对象需要是多态的。这样做的原因是dynamic_cast需要在某个地方存储将用于执行强制转换的类型信息,它通过将信息与类的 vtable 一起存储来实现这一点。为了有一个 vtable,您需要将至少一个方法设为虚拟。

解决这个问题的最简单方法是将基类析构函数标记为虚拟。

向上转换(即派生到基数)不需要转换,因为编译器能够检查转换是否在编译时起作用。但是,在向下转换时情况并非如此。

于 2011-01-10T08:25:26.507 回答
6

从 5.2.7/1 [expr.dynamic.cast] 开始:

表达式dynamic_cast<T>(v)的结果是将表达式 v 转换为类型 T 的结果。

[...]

如果 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;
}
于 2011-01-10T08:10:10.650 回答
0

Dynamic_cast

  • 它用于将基指针转换为派生指针。如果基指针不指向派生类型的对象,则返回
  • 它用于将基本引用转换为派生引用。如果引用未指向派生对象,则抛出 std::bad_cast。
  • 它可以被认为是等效于 static_cast 的检查强制转换,因为它检查指向的对象是否真的是派生类型。

您必须在此处阅读有关 Dynamic_cast(带有示例)的更多信息。

于 2011-01-10T09:21:00.777 回答
0
B* b = new D();
D* d = dynamic_cast<D*>(b);

在上面的例子中,大多数编译器会通过检查 b 的 vtable 指针是否指向派生类 D 的 vtable 来实现动态转换。如果是,它只是返回 b 的地址作为返回值,否则它返回一个 nullptr。这是执行动态演员表时可能在幕后发生的事情:-

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();
        }
    }
}

Now, if the class is not polymorphic, there is no way for the compiler to find whether pCar is pointing to honda or toyota car. Note that this is just one of the ways to implement dynamic_cast as the C++ standard does not talk anything about vtables.

于 2016-03-04T05:56:53.540 回答