Virtual functions and cast to void and back

Currently I am working with a legacy c++ code base. In this codebase pointer to objects are converted to void-pointers and then stored in a c-library. Consider the following code:

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;}
};

The objects interface and debug_interface are allocated on the heap and the address is stored to a void pointer. At some point the pointers are retrieved and then casted back to the base-class interface . Then the virtual function call is invoked. See

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;
}

First of all I don't understand, why reinterpret_cast is used. As far as I know, pointer-to-objects can be implicitly converted to void*. Furthermore, to make this cast explicit, a static_cast would be enough, right? But the more important question: Is it really save to cast the pointer debug_handle to interface* ( not to debug_interface*) and invoke the virtual call? According to the c++-standard, (5.2.10) this is undefined behavior:

A pointer to an object can be explicitly converted to a pointer to a different object type.69 When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast(static_cast(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.

The conversion from handle to foo1 should be ok, but I can again use a static_cast?

Edit My example source code was wrong. debug_interface is a derived class of interface.


Disclaimer: This first part was written when the two interfaces were not related by inheritance.

The undefined behaviour actually happens here:

   foo2->foo();

Here, you are using the interface API on a pointer to an object that is not implementing this API. The fact that both interface and debug_interface happen to implement the foo() member as their first method does not change anything: those classes are not related by inheritance, so they are not compatible.

The extract you are citing treats about cases where the conversion itself is allowed. In your case, my understanding is that you can actually convert a pointer to debug_interface to a pointer to interface : yet, the only safe thing you can now do with you pointer to interface is to convert it back to a debug_interface pointer: using it to access interface members is unsafe.


EDIT: If debug_interface publicly derives from interface , this is a different problem.

In this case, it would be totally safe to cast from debug_interface* to interface* : the derived-to-base conversion can even be applied implicitly by the compiler. Yet to be safe, this cast must be done directly , through either:

  • a static_cast
  • a dynamic_cast (which would introduce runtime check, being overkill for an upcast).
  • Doing it through two reinterpret_cast is an undefined behaviour: it is likely to work for single inheritance (on some compilers), but it is absolutely not guaranteed by the standard.
    Doing it through two static_cast would also be an undefined behaviour. The standard guarantees that (emphasis mine):

    A value of type pointer to object converted to “pointer to cv void” and back to the original pointer type will have its original value.

    In your example, you are not converting back to the original pointer, but to another pointer type: the standard is not giving you any guarantee about the value you will get.

    Possible solution

    Knowing that:

  • It is safe to directly convert from debug_interface to interface
  • It is safe to convert from a pointer to object type to void * then back to pointer to the same object type.
  • You could assemble that to get a standard guaranteed solution:

    // 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);
    

    You are right, it is undefined. When you cast back a void* you should use always the original pointer type. And you should use static_cast instead of dynamic_cast .

    So your code could be safely written as:

    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;
    }
    

    And indeed if you happen to have a class such as:

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

    And then write:

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

    You'll see why the UB.


    This is not allowed by the standard. The correct way to store the pointer would be:

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

    You can of course do it in one line:

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

    but this is too unwieldy and error-prone to my taste.

    The standard allows you to cast a pointer to void* and back, but you have to cast it back to the exact same pointer type you've started with. Pointer to a base class is no substitute.

    Your code has a good chance to crash and burn if you make debug_interface use multiple or virtual inheritance, but even with plain single inhetitance it's not conforming.

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

    上一篇: 4种不同类型的数据类型可以在c ++中采用(或通常采用)

    下一篇: 虚拟功能并投入无效并返回