将Derived*转换为void*再转换为Base*的static_cast

8

我想将派生类成员的指针转换为void*,然后再将其转换为基类的指针,就像下面的示例中所示:

#include <iostream>

class Base
{
    public:
       void function1(){std::cout<<"1"<<std::endl;}
       virtual void function2()=0;
};

class Derived : public Base
{
    public:
       virtual void function2(){std::cout<<"2"<<std::endl;}
};

int main()
{
    Derived d;
    void ptr* = static_cast<void*>(&d);
    Base* baseptr=static_cast<Base*>(ptr);
    baseptr->function1();
    baseptr->function2(); 
}

这可以编译并产生所需的结果(分别打印出12),但这是否有保障?我在这里找到的static_cast描述: http://en.cppreference.com/w/cpp/language/static_cast 只提到转换为void*,还要返回指向相同类的指针(第10点)。
3个回答

8
在一般情况下,通过静态转换将基类转换为void类型到派生类(或反之亦然)是不安全的。
在某些情况下,它几乎肯定会起作用:如果涉及的所有内容都是pod或标准布局,并且仅涉及单一继承,则事情应该没问题,至少在实践中如此。我没有从标准得出结论,但一般的想法是,这种情况下基类保证是派生类的前缀,并且它们将共享地址。
如果您想看到这种失败,请混合使用虚拟继承、多重继承(包括虚拟和非虚拟)以及非平凡的多重实现继承。基本上,当不同类型视图的“this”的地址不同时,“void”从一个类型强制转换回另一种类型,注定会失败。我曾经看到过这种失败的情况,而且它可以失败(由于远离转换点的代码库的更改),这就是为什么您要谨慎地始终将指向相同类型的void指针进行强制转换的原因。

3

一般来说,不是安全的。

假设将Derived*直接转换为Base*会导致不同的地址(例如,涉及多重继承或虚拟继承)。

现在,如果在它们之间插入一个void*的转换,编译器如何知道如何将该void*转换为适当的Base*地址呢?

如果需要将Derived*转换为void*,则应首先显式地将void*转换回原始的Derived*类型。 (从那里开始,从Derived*Base*的转换是隐式的,因此您最终会得到相同数量的转换,因此实际上并不方便。)


0

从您提供的链接中:

9) 某个类D的成员指针可以向上转型为其基类B的成员指针。这种static_cast不会检查指向对象的运行时类型中是否实际存在该成员。

这意味着只要在进行upcast之前确定它是安全的,它就保证能够工作。这就是为什么应该使用dynamic_cast,如果失败则返回nullptr

可以这样理解,如果您有:

Type * t1;

static_cast 将 t1 转换为其派生类之一,无法在编译时确定,除非对程序进行深入分析(显然不应该这样做),因此即使最终结果正确,也无法进行检查。 dynamic_cast 在运行时执行额外的工作以检查转换是否成功,因此具有动态前缀。


但我正在进行向基类的上转型。我知道这总是安全的,但我担心中间的强制转换为void* - Roberto
@Roberto 如果你知道这是一个有效的转换,那么它是安全的;如果转换有失败的可能性,那么它就不安全。 - aaronman
我知道 static_cast<Base*>(Derived*) 是安全的 - 你是说那么,static_cast<Base*>(static_cast<void*>(Derived*)) 也是安全的吗? - Roberto
@Roberto 嗯,从失败中没有返回错误代码来看并不安全。 - aaronman
任何有关 DV 的评论 - aaronman
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接