64

我有三个班级:

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
4

7 回答 7

104

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

简单层次模型的经典表示是包含:如果从该对象B派生,A则该B对象实际上将包含一个A子对象及其自身属性。

使用此模型,向下转换是通过编译时已知的偏移量进行的简单指针操作,这取决于B.

这就是static_cast所做的:静态转换被称为静态,因为转换所需的计算是在编译时完成的,无论是指针算术还是转换 (*)。

然而,当virtual继承开始时,事情往往会变得更加困难。主要问题是通过virtual继承,所有子类共享子对象的相同实例。为了做到这一点,B将有一个指向 的指针A,而不是一个A正确的,并且A基类对象将在B.

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

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

总之:

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

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

(*)正如@curiousguy 在评论中所指出的,这仅适用于向下转换。Astatic_cast允许向上转换,而不管虚拟继承还是简单继承,尽管如此转换也是不必要的。

于 2010-09-20T07:02:26.423 回答
13

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

于 2010-09-19T19:06:38.830 回答
6

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

于 2010-09-19T19:20:07.817 回答
4

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

于 2010-09-19T19:28:34.897 回答
4

根据标准文档,

5.2.9 - 9节,对于静态投射

“指向 cv1 B 的指针”类型的右值,其中 B 是类类型,可以转换为“指向 cv2 D 的指针”类型的右值,其中 D 是从 B 派生的类(第 10 条),如果一个有效的标准存在从“指向 D 的指针”到“指向 B 的指针”的转换 (4.10),cv2 与 cv1 具有相同的 cv 限定或大于 cv1 的 cv 限定,并且B 既不是 D 的虚拟基类也不是基类D的虚拟基类。

因此,这是不可能的,您应该使用dynamic_cast...

于 2010-09-20T06:01:29.103 回答
1

$5.2.9/2-“如果声明“T t(e);”,则表达式 e 可以使用 static_cast(e) 形式的 static_cast 显式转换为类型 T;对于一些发明的临时变量 t (8.5) 是良构的。”

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

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

于 2010-09-20T13:07:15.233 回答
1

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

假设

B 派生自 A(和 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 以确保我们正在用 2 个相等的类做某事后断言)并且作为你看我把自己限制在“const”方法上。因此,这将适用于“非常量”方法,但如果你做错了什么,捕捉错误几乎是不可能的。即使有断言,即使它应该失败,也有 40 亿次中的 1 次成功断言的机会 (assert(ID== other->ID);)

顺便说一句.. 一个好的 OO 设计不应该需要这种东西,但在我的情况下,我尝试重构/重新设计代码而不能放弃重新解释转换的使用。一般来说,你可以避免这种事情。

于 2012-11-16T14:45:39.310 回答