2011-01-10 62 views
8

在下面的代码中,在case 1中构造obj时,我们也可以构造derived类,但它的成员函数只能由obj访问。因此,虽然在铸造(即在情况2)下,使用obj作为源,但我们可以在其中构建derived。为什么它需要obj需要多态?C++ dynamic_cast - 多态性需求和向下转换

如果我把你和我上面的描述混淆了,为什么在upcast 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 

谢谢。

回答

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; 
} 
0

认为它是这样的:当你垂头丧气,你告诉编译器你想指向一个多态类指针。但是当你使用dynamic_cast进行上传时,它会说“当你从我身上得到你时,你已经得到了我,你为什么要明确地使用dynamic_cast回到我身边?”因此编译器会给出错误。也就是说,编译器在Derived类中看到它们是Base类型的子部分,因此它知道它们不需要程序员明确地指出它的指针(并且可能会造成严重破坏)。

我希望解释有帮助。

+0

没有错,如果我明确向上,这在标准文档中清楚地说。 – Mahesh 2011-01-10 08:29:06

15

为了使dynamic_cast工作,该对象需要是多态的。原因是dynamic_cast需要某处存储将用于执行强制转换的类型信息,并通过将信息存储在类的vtable旁边来执行此操作。为了有一个虚拟桌面,你需要至少使你的一个虚拟方法成为虚拟桌面。

最简单的方法是将基类析构函数标记为虚函数。

上传(即派生到base)不需要转换,因为编译器能够检查转换在编译时是否工作。但是,向下广播时也是如此。

+1

在这个例子中,你可以使用`static_cast`向下倾倒。你知道在编译时`obj`包含正确的类。 dynamic_cast的用法是在编译时不知道对象是否来自另一个对象。例如,当你得到一个函数的指针,并需要检查返回的对象是否属于某个类。 (但你是对的,他们必须是虚拟的。) – RedX 2011-01-10 10:05:16

0

的dynamic_cast

  • 它用于一个基指针 浇铸到派生的指针。如果基址 指针未指向 派生的对象,则返回
  • 它用于将基准参考 投射到派生参考中。如果 引用未指向派生对象 ,则它会抛出 std :: bad_cast。
  • 它可以被认为是等同于static_cast的检查转换 ,因为它 检查指向 的对象是否确实是派生类型。

您必须阅读更多关于Dynamic_cast(例如)there

0
B* b = new D(); 
D* d = dynamic_cast<D*>(b); 

在上述例子中大多数编译器将通过检查实现动态转换是否乙组分的派生类d与否的V表V表指针。 如果是,它只是返回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(); 
     } 
    } 
} 

现在,如果类不是多态的,有没有办法让编译器找到PCAR是否指向本田或丰田汽车。请注意,这只是实现dynamic_cast的方法之一,因为C++标准没有讨论关于vtable的任何内容。