C ++无法通过虚拟基础A从基础A转换为派生类型B.

时间:2010-09-19 19:04:30

标签: c++ casting downcast virtual-inheritance static-cast

我有三个班级:

class A {};

class B : virtual public A {};
class C : virtual public A {};

class D: public B, public C {};

尝试从A *到B *的静态强制转换我得到以下错误:

cannot convert from base A to derived type B via virtual base A

7 个答案:

答案 0 :(得分:85)

为了理解演员系统,您需要深入了解对象模型。

简单层次结构模型的经典表示是包含:如果B派生自A,那么B对象实际上将包含一个A子对象属性。

使用此模型,向下转换是一种简单的指针操作,由编译时已知的偏移量决定,该偏移量取决于B的内存布局。

这是 static_cast 的作用:静态强制转换被称为静态,因为在编译时计算演员必需的内容,无论是指针算术还是转换(*)。 / p>

然而,当virtual继承踢的事情往往变得有点困难。主要问题是,使用virtual继承,所有子类共享子对象的相同实例。为此,B将指向A,而不是AA基类对象将在{B之外实例化。 1}}。

因此,在编译时不可能推导出必要的指针算法:它取决于对象的运行时类型。

每当存在运行时类型依赖项时,您需要RTTI(运行时类型信息),并且使用RTTI进行强制转换是 dynamic_cast 的工作。

总结:

  • 编译时downcast:static_cast
  • 运行时向下转发:dynamic_cast

另外两个也是编译时演员,但它们非常具体,很容易记住它们的用途......而且它们很臭,所以最好不要再使用它们。

(*)正如@curiousguy在评论中指出的那样,这仅适用于向下转换。无论虚拟或简单继承,static_cast都允许向上转换,但演员也是不必要的。

答案 1 :(得分:12)

据我所知,您需要使用dynamic_cast,因为继承是virtual而您正在向下转发。

答案 2 :(得分:6)

在这种情况下你不能使用static_cast因为编译器在编译时不知道B相对于A的偏移量。必须根据最派生对象的确切类型在运行时计算偏移量。因此,您必须使用dynamic_cast

答案 3 :(得分:4)

是的,您必须使用dynamic_cast,但您必须使基类A具有多态性,例如:通过添加虚拟dtor。

答案 4 :(得分:4)

根据标准文件,

5.2.9 - 9 静态演员

  

“指向cv1 B的指针”类型的右值,其中B是类类型,可以转换为类型为“指向cv2的指针”的右值   D,“其中D是来自B的派生类(第10节),如果从”指向D的指针“到”指向B的指针“的有效标准转换   存在(4.10),cv2与cv1相同,或者cv资格比cv1更高,而 B既不是虚拟基类   D也不是D的虚拟基类的基类。

因此,您无法使用dynamic_cast ...

答案 5 :(得分:1)

  

$ 5.2.9 / 2-“表达式e可以   显式转换为类型T使用   表单的static_cast   static_cast(e)如果声明   对于某些人来说,“T t(e);”格式正确   发明了临时变量t(8.5)。“

在您的代码中,您尝试使用'T = B *'和'e = A *'

进行static_cast

现在'B * t(A *)'在C ++中的格式不正确(但'A * t(B *)'是因为'A'是'B'的虚拟明确且可访问的基础。因此,代码给出错误。

答案 6 :(得分:1)

我不知道这是否“安全”但是。

假设

B派生自A(和纯虚拟)

因为我知道指向B的指针仍然是指向B的指针。

    class A
    {
            virtual void doSomething(const void* p) const =0;
    };

    class B
    {
    public:
            int value;
            virtual void doSomething(const void*p)const
            {
            const B * other = reinterpret_cast<const B*>(p);
            cout<<"hello!"<< other->value <<endl;
            }
    };

    int main()
    {
            B  foo(1),bar(2);
            A * p = &foo, q=&bar;
            p->doSomething(q);
            return 0;
    }

这个程序执行并正确返回打印“你好!”和另一个对象的值(在这种情况下为“2”)。

顺便说一句,我正在做的事情是非常不安全的(我个人给每个类提供了一个不同的ID,并且在重新解释转换后断言当前ID等于其他ID以确保我们正在做两个相同类的事情)正如你所看到的那样,我将自己局限于“const”方法。因此,这将与“非const”方法一起使用,但如果你做错了什么,捕获bug几乎是不可能的。即使有断言,即使它应该失败,也有4亿次成功断言的机会 (断言(ID == other-&gt; ID);)

顺便说一下..好的OO设计不应该要求这样的东西,但在我的情况下,我试图重构/重新设计代码而不能放弃重新解释铸造的用法。一般来说,你可以避免这种事情。