虚拟功能并投入无效并返回

目前我正在使用遗留的c ++代码库。 在这个代码库中,指向对象的指针被转换为空指针,然后存储在一个c库中。 考虑下面的代码:

class interface {
public:
  virtual void foo() {
    std::cout << "Interface" << std::endl;}
  virtual ~interface(){};
};

class debug_interface: public interface {
public:
  virtual void foo() {
   std::cout << "Debug Interface" << std::endl;}
};

对象interfacedebug_interface分配在堆上,地址存储到一个空指针。 在某一时刻,指针被检索出来,然后被转换回基类interface 。 然后调用虚函数调用。 看到

int main(int argc, char *argv[]){

    void *handle = reinterpret_cast<void*>(new interface());
    void *debug_handle = reinterpret_cast<void*>(new debug_interface());

   //void *handle = new interface();
   //void *debug_handle = new debug_interface();

   interface *foo1 = reinterpret_cast<interface*>(handle);
   interface *foo2 = reinterpret_cast<interface*>(debug_handle);

   //interface *foo1 = static_cast<interface*>(handle);
   //interface *foo2 = static_cast<interface*>(debug_handle);

   foo1->foo();
   foo2->foo();

   return 0;
}

首先我不明白,为什么使用reinterpret_cast。 据我所知,指向对象可以隐式转换为void *。 此外,为了使这个演员明确,static_cast就足够了,对吧? 但更重要的问题是:将指针debug_handle强制转换为接口*(而不是debug_interface *)并调用虚拟调用是否真的节省了成本? 根据c ++标准(5.2.10),这是未定义的行为:

指向对象的指针可以显式转换为指向不同对象类型的指针.69当“指向T1的指针”类型的prvalue v被转换为类型“指向cv T2的指针”时,结果为static_cast(static_cast(v ))如果T1和T2都是标准布局类型(3.9),并且T2的对齐要求不比T1更严格。 将“指向T1的指针”类型的prvalue转换为类型“指向T2的指针”(其中T1和T2是对象类型,并且T2的对齐要求不比T1的对齐要求更严格)并返回到其原始类型,指针值。 任何其他此类指针转换的结果都未指定。

handlefoo1的转换应该没问题,但我可以再次使用static_cast?

编辑我的示例源代码是错误的。 debug_interface是一个派生的接口类。


免责声明:第一部分是在两个接口不通过继承关联时编写的。

未定义的行为实际发生在这里:

   foo2->foo();

在此,您正在使用interface API指向实现此API的对象。 interfacedebug_interface碰巧实现了foo()成员,因为它们的第一个方法不会改变任何东西:这些类不是通过继承关联的,所以它们不兼容。

您引用的摘录将讨论允许转换本身的情况。 在你的情况下,我的理解是,你实际上可以将一个指向debug_interface的指针转换为一个指向interface的指针:然而,你现在可以用指针指向接口的唯一安全的事情就是将它转换回debug_interface指针:使用它来访问interface成员是不安全的。


编辑:如果debug_interface公开从interface派生,这是一个不同的问题。

在这种情况下,从debug_interface*interface*是完全安全的:编译器可以隐式应用派生到基本的转换。 为了安全起见,这个演员必须通过以下任一方式直接完成:

  • 一个static_cast
  • 一个dynamic_cast (它将引入运行时检查,对于upcast来说是过度的)。
  • 通过两次reinterpret_cast做它是一个未定义的行为:它可能适用于单继承(在某些编译器上),但它绝对不能由标准保证。
    通过两个static_cast做它也将是一个未定义的行为。 标准保证(重点是我的):

    将指向对象的类型指针的值转换为“指向cv void的指针”并返回到原始指针类型将具有其原始值。

    在你的例子中,你不会转换回原来的指针,而是转换为另一个指针类型:标准不会保证你将得到的值。

    可能的方案

    知道:

  • 直接从debug_interface转换到interface是安全的
  • 将指针转换为对象类型为void *然后返回指向相同对象类型的指针是安全的。
  • 你可以将它组装起来以获得标准的有保证的解

    // Safe, see point #1.
    // The new expression returns a debug_interface* and static_cast applies a derived-to-base conversion.
    interface *debug_handle_interim = static_cast<interface*>(new debug_interface());
    
    // Convert a interface* to void* then back to interface*, see #2
    void *type_erased = static_cast<void*>(debug_handle_interim);
    interface *debug_handle_back = static_cast<interface*>(type_erased);
    

    你是对的,它是未定义的。 当你抛出一个void*你应该总是使用原始的指针类型。 你应该使用static_cast而不是dynamic_cast

    所以你的代码可以写成:

    int main(int argc, char *argv[]){
    
        void *handle = new interface();
        void *debug_handle = static_cast<interface*>(new debug_interface());
    
        //Beware! This would be wrong:
        //void *debug_handle = new debug_interface();
    
       interface *foo1 = static_cast<interface*>(handle);
       interface *foo2 = static_cast<interface*>(debug_handle);
    
       foo1->foo();
       foo2->foo();
    
       return 0;
    }
    

    事实上,如果你碰巧有一个类,如:

    class weird_interface : something, public interface
    { /* */};
    

    然后写:

    weird_interface *a = new weird_interface();
    interface *b = static_cast<interface*>(static_cast<void*>(a));
    interface *c = a;
    static_assert(b == c, "");
    

    你会明白为什么UB。


    这是标准所不允许的。 存储指针的正确方法是:

    interface* tmp = new debug_interface;
    void* handle = reinterpret_cast<void*>(tmp);
    

    你当然可以在一行中做到这一点:

    void* handle = reinterpret_cast<void*>(
        static_cast<interface*>(new debug_interface));
    

    但这太笨拙了,容易出错。

    该标准允许您将一个指针指向void*并返回,但是您必须将其转换回与您开始使用的完全相同的指针类型。 指向基类的指针不能替代。

    如果您使debug_interface使用多重或虚拟继承,您的代码很有可能会崩溃并烧毁,但即使使用普通单一inhetitance,它也不符合要求。

    链接地址: http://www.djcxy.com/p/28761.html

    上一篇: Virtual functions and cast to void and back

    下一篇: casting away the constness using const