How to Use Memcpy in C++ to Copy Classes That Have No Pointers or Virtual Functions

Can I use memcpy in C++ to copy classes that have no pointers or virtual functions

According to the Standard, if no copy constructor is provided by the programmer for a class, the compiler will synthesize a constructor which exhibits default memberwise initialization. (12.8.8) However, in 12.8.1, the Standard also says,

A class object can be copied in two
ways, by initialization (12.1, 8.5),
including for function argument
passing (5.2.2) and for function value
return (6.6.3), and by assignment
(5.17). Conceptually, these two
operations are implemented by a copy
constructor (12.1) and copy assignment
operator (13.5.3).

The operative word here is "conceptually," which, according to Lippman gives compiler designers an 'out' to actually doing memberwise initialization in "trivial" (12.8.6) implicitly defined copy constructors.

In practice, then, compilers have to synthesize copy constructors for these classes that exhibit behavior as if they were doing memberwise initialization. But if the class exhibits "Bitwise Copy Semantics" (Lippman, p. 43) then the compiler does not have to synthesize a copy constructor (which would result in a function call, possibly inlined) and do bitwise copy instead. This claim is apparently backed up in the ARM, but I haven't looked this up yet.

Using a compiler to validate that something is Standard-compliant is always a bad idea, but compiling your code and viewing the resulting assembly seems to verify that the compiler is not doing memberwise initialization in a synthesized copy constructor, but doing a memcpy instead:

#include <cstdlib>

class MyClass
{
public:
MyClass(){};
int a,b,c;
double x,y,z;
};

int main()
{
MyClass c;
MyClass d = c;

return 0;
}

The assembly generated for MyClass d = c; is:

000000013F441048  lea         rdi,[d] 
000000013F44104D lea rsi,[c]
000000013F441052 mov ecx,28h
000000013F441057 rep movs byte ptr [rdi],byte ptr [rsi]

...where 28h is the sizeof(MyClass).

This was compiled under MSVC9 in Debug mode.

EDIT:

The long and the short of this post is that:

1) So long as doing a bitwise copy will exhibit the same side effects as memberwise copy would, the Standard allows trivial implicit copy constructors to do a memcpy instead of memberwise copies.

2) Some compilers actually do memcpys instead of synthesizing a trivial copy constructor which does memberwise copies.

Will memcpy or memmove cause problems copying classes?

In the general case, yes, there will be problems. Both memcpy and memmove are bitwise operations with no further semantics. That might not be sufficient to move the object*, and it is clearly not enough to copy.

In the case of the copy it will break as multiple objects will be referring to the same dynamically allocated memory, and more than one destructor will try to release it. Note that solutions like shared_ptr will not help here, as sharing ownership is part of the further semantics that memcpy/memmove don't offer.

For moving, and depending on the type you might get away with it in some cases. But it won't work if the objects hold pointers/references to the elements being moved (including self-references) as the pointers will be bitwise copied (again, no further semantics of copying/moving) and will refer to the old locations.

The general answer is still the same: don't.


* Don't take move here in the exact C++11 sense. I have seen an implementation of the standard library containers that used special tags to enable moving objects while growing buffers through the use of memcpy, but it required explicit annotations in the stored types that marked the objects as safely movable through memcpy, after the objects were placed in the new buffer the old buffer was discarded without calling any destructors (C++11 move requires leaving the object in a destructible state, which cannot be achieved through this hack)

memcpy array of classes revisit

isn't std::is_trivially_copyable sufficient to allow the memcpy of an array of classes?

Yes, it is sufficient. Your example has no UB.

That said, you don't necessarily need to restrict yourself to using std::memcpy because you can use std::copy_n (or std::copy) instead, and that will work with all copyable types, not just trivial ones:

static_assert(std::size(Array1) >= std::size(Array2));
// no need for asserting for triviality
std::copy_n(Array2, std::size(Array2), Array1);

Or, if you wrap the arrays inside a class, then you can use assignment. The standard library has a template for such array wrapper:

std::array<MyClass, 10> Array1;
std::array<MyClass, 10> Array2;
Array1 = Array2;

In case you were expecting that std::memcpy is faster, there is no need to worry. A decent optimiser will produce the same output, likely inlining the call to memcpy as well: https://godbolt.org/z/nEvGMe


P.S. Instead of the hardcoded 10 * sizeof(MyClass), I recommend sizeof Array1.

Why are C++ classes with virtual functions required to have a non-trivial copy constructor?

Are there any reasons I did not think of from implementation perspective why such classes should have non-trivial copy-constructors?

There is quite obvious one: copy-constructor of I is not trivial. And it is not final, so there can be other derived classes. So it must be non-trivial and set virtual table pointer properly after memcpy, as there could be derived classes relying on it.

Are there any reasons the restrictions on triviality for copy-constructors cannot be relaxed in the standard?

1) Constructor triviality part was simply not revised with inclusion of final keyword.

2) People think that keywords like delete, final and overrride should help avoid most common errors, and clarify programmer intention, not change behavior of the program.

3) It complicates language:
A constructor is trivial, unless you have virtual function, then it is nontrivial, unless your class is final, then it is trivial again, unless something else, then it is not, unless...

4) Nobody though that it is worth writing formal paper for, proving usefulness of this addition to Committee and pushing this change into language.

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

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

Copying structure in C with assignment instead of memcpy()

Both methods are equivalent, and perform a shallow copy. This means that the structure itself is copied, but anything the structure references is not copied.

As for why memcpy is more popular, I'm not sure. Older versions of C did not support structure assignment (although it was a common extension as early as 1978), so perhaps the memcpy style stuck as a way of making more portable code? In any case, structure assignment is widely supported in PC compilers, and using memcpy is more error-prone (if you get the size wrong, Bad Things are likely to happen), and so it's best to use structure assignment where possible.

There are, however, cases where only memcpy works. For example:

  • If you're copying a structure to or from an unaligned buffer - eg, to save/load to/from disk or send/receive on a network - you need to use memcpy, as structure assignment requires both source and destination to be aligned properly.
  • If you're packing additional information after a structure, perhaps using a zero-element array, you need to use memcpy, and factor this additional information into the size field.
  • If you're copying an array of structures, it may be more efficient to do a single memcpy rather than looping and copying the structures individually. Then again, it may not. It's hard to say, memcpy implementations differ in their performance characteristics.
  • Some embedded compilers might not support structure assignment. There's probably other more important things the compiler in question doesn't support as well, of course.

Note also that although in C memcpy and structure assignment are usually equivalent, in C++ memcpy and structure assignment are not equivalent. In general C++ it's best to avoid memcpying structures, as structure assignment can, and often is, overloaded to do additional things such as deep copies or reference count management.

memcpy derived class to base class, why still called base class function

What you are doing is illegal in C++ language, meaning that the behavior of your b_memcpy object is undefined. The latter means that any behavior is "correct" and your expectations are completely unfounded. There's not much point in trying to analyze undefined behavior - it is not supposed to follow any logic.

In practice, it is quite possible that your manipulations with memcpy did actually copy Derived's virtual table pointer to b_memcpy object. And your experiments with b_ref confirm that. However, when a virtual method is called though an immediate object (as is the case with b_memcpy.vfunc() call) most implementations optimize away the access to the virtual table and perform a direct (non-virtual) call to the target function. Formal rules of the language state that no legal action can ever make b_memcpy.vfunc() call to dispatch to anything other than Base::vfunc(), which is why the compiler can safely replace this call with a direct call to Base::vfunc(). This is why any virtual table manipulations will normally have no effect on b_memcpy.vfunc() call.



Related Topics



Leave a reply



Submit