C++ Dynamic_cast - 多态要求和向下转型

发布于 2024-10-11 08:16:49 字数 659 浏览 8 评论 0原文

在下面的代码中,在情况 1 中构造 obj 时,我们也构造了一个派生类对象,但它的成员函数对于 obj 来说是无法访问的。因此,在向下转换时(即在情况 2 中),使用 obj 作为源,我们已经在其中构造了派生的。为什么 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

In the following code, while constructing obj in case 1, we construct a derived class object too, but its member functions are just inaccessible to obj. So while downcasting (i.e., in case 2), using obj as source, we have the constructed derived in it already. Why would obj need to be polymorphic?

If I confused you with my above description, why doesn't obj need to be polymorphic when upcasting, but while downcasting it does need to be polymorphic while using 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

凶凌 2024-10-18 08:16:49

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

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

向上转换(即派生到基数)不需要强制转换,因为编译器能够检查强制转换在编译时是否有效。然而,当情绪低落时,情况却并非如此。

In order for dynamic_cast to work the object needs to be polymorphic. The reason for this is that dynamic_cast needs somewhere to store the type information that is will use to perform the cast, and it does this by storing the information alongside the vtable for the class. In order for there to be a vtable you need to make at least one of your methods virtual.

The easiest way around this is to flag the base class destructor as virtual.

Upcasting (ie derived to base) doesn't needs a cast as the compiler is able to check that the cast would work at compile time. However, the same isn't true when downcasting.

深海不蓝 2024-10-18 08:16:49

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

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

[...]

如果 T 是“指向 cv1 B 的指针”并且 v 具有“指向 cv2 D 的指针”类型,使得 B 是 D 的基类,则结果是 a
指向 v 所指向的 D 对象的唯一 B 子对象的指针。

[...]

否则,v 应为指向多态类型的指针或多态类型的左值。

该标准甚至提供了以下示例,说明多态类型要求并不代表派生到基类的转换:

struct B {};
struct D : B {};
void foo(D* dp)
{
    B* bp = dynamic_cast<B*>(dp); // equivalent to B* bp = dp;
}

From 5.2.7/1 [expr.dynamic.cast] :

The result of the expression dynamic_cast<T>(v) is the result of converting the expression v to type
T.

[...]

If T is "pointer to cv1 B" and v has type "pointer to cv2 D" such that B is a base class of D, the result is a
pointer to the unique B sub-object of the D object pointed to by v.

[...]

Otherwise, v shall be a pointer to or an lvalue of a polymorphic type.

The standard even provides the following example which illustrates that the polymorphic type requirement does not stand for derived to base conversion :

struct B {};
struct D : B {};
void foo(D* dp)
{
    B* bp = dynamic_cast<B*>(dp); // equivalent to B* bp = dp;
}
偏爱自由 2024-10-18 08:16:49
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();
        }
    }
}

现在,如果该类不是多态的,则编译器无法确定 pCar 是否指向本田汽车或丰田汽车。请注意,这只是实现dynamic_cast 的方法之一,因为C++ 标准没有谈论vtable。

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

In the above example most compilers would implement dynamic cast by checking whether the vtable pointer of b points to the vtable of the derived class D or not.
If yes, it simply returns the address of b as the return value otherwise it returns a nullptr.
This is what possibly goes on behind the scenes when a dynamic cast executes :-

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.

夜声 2024-10-18 08:16:49

Dynamic_cast

  • 用于转换基指针
    转化为派生指针。如果基地
    指针不指向以下对象
    派生的类型,它返回
  • 它用于转换基引用
    进入派生参考。如果
    引用未指向对象
    派生的,它抛出
    std::bad_cast。
  • 可以认为是checkedcast
    相当于 static_cast,因为它
    检查对象是否指向
    确实是派生类型。

您必须阅读有关 Dynamic_cast 的更多信息(带有示例)那里

Dynamic_cast

  • It is used to cast a base pointer
    into a derived pointer. If the base
    pointer doesn't point to an object of
    the type of the derived, it returns
  • It is used to cast a base reference
    into a derived reference. If the
    reference isn't pointing to an object
    of the derived, it throws
    std::bad_cast.
  • It can be considered the checked cast
    equivalent to static_cast, in that it
    checks whether the object pointed to
    really is of the derived type.

You must read more about Dynamic_cast (with example) there.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文