Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When memcpy from this to a new object in a child class, warning "destination for this 'memcpy' call is a pointer to dynamic class..." shows

I'm going to create some parent and child classes with virtual copy function, which returns copy of self:

class A{
    public:
    int ID;
    virtual A* copy(){
        return new A();
    }
}

class B : public A{
     public:
     int subID;
     virtual A* copy(){
         B* b=new B();
         memcpy(b,this,sizeof(B));
         return b;
      }
};

When it compiles, it shows the following warning:

destination for this 'memcpy' call is a pointer to dynamic class 'B' ; vtable pointer will be overwritten
explicitly cast the pointer to silence this warning

What does this warning mean and what potential problems does it cause?

like image 542
Gstestso Avatar asked Oct 14 '25 18:10

Gstestso


2 Answers

It means that this will not work. C++ objects should not be copied with the C library's memcpy() function (except in certain limited situations), which knows nothing about C++ classes, their constructors, destructors, virtual methods, and everything else that's in C++ that's not in C.

What you want is a copy constructor. It's job is exactly what you're trying to accomplish: to make a copy of an existing object.

virtual A* copy(){
         B* b=new B(*this);
         return b;
      }
like image 64
Sam Varshavchik Avatar answered Oct 17 '25 08:10

Sam Varshavchik


It is warning, not an error. And that's for a reason: it depends on what you actually do precisely with memcpy() whether or not it would cause any misbehaviour with dynamic objects.

So yes, copy by assignment/constructor is safer than by raw memory copy on C++ objects with virtual methods (and hence use that on doubt instead), but no using memcpy() for them is not necessarily always an error. If you use memcpy() correctly on them you won't get misbehaviours.

In detail:

With most compilers, dynamic C++ objects automatically get an additional member called the vpointer, which is usually generated by compilers as the very first member of the object, so at the very beginning of the object's memory location. The vpointer is just a pointer, that is a memory address where the class's vtable resides. You can easily check this:

#include <stdlib.h>
#include <stdint.h>

// Non-dynamic class.
class Foo {
public:
    int a;

    void asdf() {}
};

// Dynamic class.
class Bar {
public:
    int a;

    virtual void asdf() {}
};

int main(int argc, char **argv) {
    Foo foo;
    Bar bar;

    printf("&foo=%p &foo.a=%p delta=%lld\n", &foo, &foo.a, uint64_t(&foo.a) - uint64_t(&foo));
    printf("&bar=%p &bar.b=%p delta=%lld\n", &bar, &bar.a, uint64_t(&bar.a) - uint64_t(&bar));

    return 0;
}

When you run this code, you'll see that member variables a of class Foo have the exact same memory address as the object itself. Whereas member variables a of class Bar have a memory address with offset (e.g. +8). That's because the compiler injected the vpointer as first member variable to the object of class Bar (as kind of hidden member variable).

The (read-only) vtable itself (where the object's vpointer points to) is shared on a per-class level by all objects of a specific dynamic class, and the vtable is used to translate a virtual method name to one specific implementation of that virtual method.

So when you memcpy() dynamic objects, you have to carefully deal with the fact that the object contains a vpointer. Just copying an array of dynamic objects from one memory location to another while really copying the entire objects and preserving their type, is usually safe. A common use case for this is efficiently increasing the size of a container containing dynamic objects by memcpy()-ing them to a newly malloc()-ated memory chunk with bigger size and then free()-ing the old chunk.

A problematic example on the other hand is when you try to memcpy() an object from one dynamic class to an object of a different dynamic class. In this case you would at least overwrite the vpointer and change the class type, so different methods might be executed before vs. after memcpy(). But yet again: it depends on your use case whether that's a "misbehaviour". It might even be your intention to change the class type (including object's mappings to virtual methods).

Also: as you can see from the code example above, when you memcpy() from an object of a dynamic class to a non-dynamic one and vice versa, you must be aware about the difference in memory layout due to the injected vpointer. Especially the existence vs. non-existence of vpointer in this example is tricky. I mean it is quite common to force a C-style type cast of pointers once in a while which then might not do what you had in mind due to that.

Another issue is compatibility: officially C++ does not mandate how exactly dynamic objects shall be implemented by compilers. So even though the things I wrote here do apply to essentially all major compilers, officially C++ does not even require the existence of vtable's or vpointers. So theoretically compilers are free to implement dynamic objects in completely other ways, which might yet create a different assessment of this overall issue. But that theoretical issue can easily be guarded by writing test cases for your software.

like image 37
Blub Avatar answered Oct 17 '25 09:10

Blub