从基类到不同派生类的多重继承转换

问题描述 投票:13回答:5

我们假设有这样的类层次结构:

class A //base class

class B //interface

class C : public A, public B

然后创建C对象:

A *object = new C();

可以将对象转换为B吗?

重要提示:我假设我不知道该对象是C.我只知道它实现了接口B.

c++ casting multiple-inheritance
5个回答
13
投票

不可以。这是不可能的(从A*直接铸造到B*)。

因为AB的地址位于class C的不同位置。演员阵容总是不安全,可能会出现意想不到的行为。 Demo

铸造应该总是通过class C。例如

A* pa = new C();
B* pb = static_cast<C*>(pa);
                   ^^^^ go through class C

Demo


9
投票

从任何类型到任何其他类型的方式是dynamic_cast。但它要求对象具有多态性。通常,这需要将v表与AB相关联,因此:如果A和B至少有一个虚函数,并且RTTI未禁用,

A* pa1 = new C;
A* pa2 = new A;

B* pb1 = dynamic_cast<B*>(pa1);
B* pb2 = dynamic_cast<B*>(pa2);

将导致pb2为null,并且pb1指向包含* pa1作为其A部分的对象的B部分。 (它是C或其他任何来自这两个碱基的事实并不重要)。

否则,在所有需要静态的情况下,你必须通过C

B* pb = static_cast<B*>(static_cast<C*>(pa));

请注意,static_cast<B*>(pA)无法编译,因为A和B彼此无关。


3
投票

是的,你应该首先将static_cast对象转换为C *,然后你可以将static_cast再次转换为B(虽然最后一次转换不需要,因为是标准转换)。我不确定static_casting对象直接对B是否有效,请尝试查看是否出现编译错误。 reinterpret_casting对象到B会让你遇到运行时崩溃,因为如果A和B都是非空的,它们会有不同的地址。

编辑更改问题后,将无法再执行所需操作。您需要知道继承树上下的正确路径,因为在具有多个继承和非空类的场景中进行转换意味着指针的移位。


0
投票

只要对象派生自B,就可以始终将对象强制转换为B.当然,您只能调用接口B中定义的方法,因为虚拟指针只能访问虚拟表中B定义的方法。


0
投票

当然可以。从逻辑上讲,如果您确定对象X是A的类型,那么这意味着您可以将其用作A.

实现这一目标的简单而天真的方法是使用标准C ++提供的dynamic_cast。然而,它将使用线性时间来查看vtable,因为dynamic_cast需要检查给定指针是否实际上可以被转换为给定类型,而不是像已经知道X是A类型的那些人。某些平台可能不提供RTTI ,让你可以做dynamic_cast。

还有另一个解决方案:让A和B中的一个知道它本身可能是一个超类,它正在进行多重继承。

#include <iostream>
#include <string>

struct Widget
{
    virtual ~Widget() = default;

    double widgetData;
};

struct DbItem
{
    virtual ~DbItem() = default;

    std::string nodeData;
};

struct GeneralItem
{
    virtual ~GeneralItem() = default;

    virtual void* cast(int type) = 0;
//  virtual void const* cast(int type) const = 0; // Use this as well

    // This is alternative for someone who don't like
    // dynamic function!
    void* ptrOfWidgetOrDbNode;

    // If GeneralItem can know what it can be casted to.
//  virtual Widget* toWidget() = 0;
//  virtual DbItem* toDbItem() = 0;
};

enum { Widget_id, DbItem_id };

// Can be used as syntax candy.
Widget* toWidget(GeneralItem* gItem)
{
    return static_cast<Widget*>(gItem->cast(Widget_id));
}
DbItem* toDbItem(GeneralItem* gItem)
{
    return static_cast<DbItem*>(gItem->cast(DbItem_id));
}

struct TextView : Widget, GeneralItem
{
    TextView() { widgetData = 20.0; }
    void* cast(int type) override
    {
        switch ( type )
        {
            // WARNING: static_cast IS MANDATORY.
            case Widget_id: return static_cast<Widget*>(this);
            default: return nullptr;
        }
    }
};

struct ImageView : GeneralItem, Widget
{
    ImageView() { widgetData = 40.0; }
    void* cast(int type) override
    {
        switch ( type )
        {
            // WARNING: static_cast IS MANDATORY.
            case Widget_id: return static_cast<Widget*>(this);
            default: return nullptr;
        }
    }
};

struct SoundData : DbItem, GeneralItem
{
    SoundData() { nodeData = "Sound"; }
    void* cast(int type) override
    {
        switch ( type )
        {
            // WARNING: static_cast IS MANDATORY.
            case DbItem_id: return static_cast<DbItem*>(this);
            default: return nullptr;
        }
    }
};

struct VideoData : GeneralItem, DbItem
{
    VideoData() { nodeData = "Video"; }
    void* cast(int type) override
    {
        switch ( type )
        {
            // WARNING: static_cast IS MANDATORY.
            case DbItem_id: return static_cast<DbItem*>(this);
            default: return nullptr;
        }
    }
};

GeneralItem* getDbItem();
GeneralItem* getWidget();

int main()
{
    {
        // This is definitely subclass of Widget, but
        // GeneralItem has no relationship with Widget!
        GeneralItem* gItem = getWidget();
        Widget* nowWidget = static_cast<Widget*>(gItem->cast(Widget_id));
        std::cout << nowWidget->widgetData << std::endl;
        delete gItem;
    }

    {
        // This is definitely DbItem!
        GeneralItem* gItem = getDbItem();
//      DbItem* nowDbItem = static_cast<DbItem*>(gItem->cast(DbItem_id));
        // You can use sugar!
        DbItem* nowDbItem = toDbItem(gItem);
        std::cout << nowDbItem->nodeData << std::endl;
        delete gItem;
    }
}

GeneralItem* getDbItem()
{
    return new VideoData;
}

GeneralItem* getWidget()
{
    return new TextView;
}

在此示例代码中,有3个基类,2个getter函数和一些子类。 Widget和DbItem是您无法触及的类,而GeneralItem旨在成为执行多重继承的类的超类。

getDbItem() : GeneralItem*是一个返回GeneralItem的函数,它也是DbItem的实例。 getWidget() : GeneralItem*是类似的,返回实例的类型必须是Widget。

GeneralItem有一个虚函数cast(type_id) : void*,它返回给定类型的指针,但是在void指针形式中,因为返回类型是在编译时确定的。它可以通过static_cast或reinterpret_cast转换回你想要的类型:任何一个都可以使用。

你需要注意cast()实现中超类和static_cast的任意顺序:如果你错过了static_cast,你的void指针将被映射到子类,而不是精确的DbItemWidget。省略static_cast将允许你干净,即使没有任何警告编译结果,因为将类型指针强制转换为void指针是完全合理的。

这只是各种选择中的一种解决方案,因此如果您正在寻找可以在不同情况下使用的解决方案,请告诉我。

我的英语不太好,上下文中可能会有一些语法错误。

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