C++: Copying Objects Using Memmove and Malloc

458 views Asked by At

I was playing around templates when I was surprised that the code below doesn't work as expected:

#include <iostream>
#include <string>
#include <cstring>

template <class Object>
class CreatorTWO {
public:
  CreatorTWO (void) {}
  ~CreatorTWO (void) throw () {}
  template <typename... Object_Params>
  Object* create (const Object_Params&... params_) {
    Object _temp_obj = Object(params_...);
    size_t _obj_bytes = sizeof (_temp_obj);
    void * _mem_block = std::malloc (_obj_bytes);
    std::memmove(_mem_block,&_temp_obj,_obj_bytes);
    //The line below prints the dereferenced object before it is returned:
    std::cout << *(static_cast <Object*> (_mem_block)) << '\n';
    return static_cast <Object*> (_mem_block);
  }
};

int main (int argc, char* argv[]) {
  CreatorTWO <std::string> _c2;
  std::string* _strp = _c2.create("Hello");
  std::cout << *_strp << '\n';
  std::free (_strp);
  return 0;
}

The code above is supposed to create an object with varying number of parameters passed to it. However, when I created an instance templated with std::string to it, passing an argument of "Hello" should give me a pointer to string that contains "Hello". However, this is not the case. If you run the code above, the pre and post values are different with the pre being the correct one. Does anyone know what causes this undesired behaviour? Thanks.

1

There are 1 answers

0
mksteve On

C++ sits in an uncomfortable place between raw access to the underlying hardware, and a high level language which is capable of rapid development.

The general principle is that you can't use memcpy to move an object, which you have broken in this case.

When you create a class.

 class Example {
     protected:
        char * ptr;
        size_t len;
     public:
        Example( const char * str ) {
            len = strlen( str );
            ptr = new char[ len  + 1];
        }
        virtual ~Example() {
             delete [] ptr;
        }
        Example & operator=( const Example & rhs ) {
            if( &rhs != this ) {
                delete [] ptr;
                len = rhs.len();
                ptr = new char[ len + 1 ];
                strcpy( ptr, rhs.ptr );
            }
        }
        Example( const Example & src ) {
            len = src.len;
            ptr = new char[ len + 1];
            strcpy( ptr, src.ptr );
        }
        Example() {
            ptr = new char[1];
            ptr[0] = '\0';
            len = 0;
        }
 };

The idea of accessing the internal state stops the designer of the class from being able to make guarantees of correctness.

If the class Example had been memcpy'ed, then there would be two instances of the class, with both having the value ptr set. If one of them is destroyed, then it free the memory addressed by ptr in the other, breaking the other classes internal state.

When a class has been well implemented, copying using the operator= allows the designer to understand what can be shared, and what needs to be copied, and can ensure correct behavior.

By modifying or copying a class using raw memory operators is heads towards undefined behavior for this reason.