C ++ copy constructor and shallow copy

Suppose I have a class with many explicit (statically allocated) members and several pointers that are allocated dynamically.

When I declare a copy constructor in witch, I make a deep copy of the manually selected elements, I would not want to copy every statically allocated explicite member.

How can I use the implicit (default) functions of the instance constructor in an explicit copy constructor?

+3
c ++
source share
6 answers

Use containment :

class outer { public: outer( const outer& other ) : members_( other_.members_ ), pmember_( deep_copy( other.pmember_ )) {} // DON'T FORGET ABOUT THESE TOO outer& operator=( const outer& ); ~outer(); private: struct inner { inner( int i, float f ) : int_( i ), float_( f ) {} int int_; float float_; }; inner members_; //< direct members something* pmember_; //< indirect member }; 
+5
source share

"multiple pointers that are highlighted dynamically."

Danger, Will Robinson! This is very difficult, of course, it’s not worth it to write a class that contains several pointers to raw, and which offers a strong guarantee of exception when executing its life cycles. For example, consider the following:

 struct MyClass { char *a; char *b; int i; float f; MyClass(int i, float f): a(0), b(0), i(i), f(f) { a = new char[12]; b = new char[23]; } ~MyClass() { delete[] a; delete[] b; } ... }; 

Now suppose that the selection b throws. The MyClass destructor is not called, so the memory allocated for a allocated. So you need to explicitly catch the exception and generally clutter your code with error handling.

Instead, define a small helper class that deals with only one pointer:

 struct MyPointerHolder { char *value; MyPointerHolder(int s) : value(new char[s]) {} ~MyPointerHolder() { delete[] value; } MyPointerHolder(const MyPointerHolder &rhs) { // perform deep copy } MyPointerHolder &operator=(const MyPointerHolder &rhs) { // sample implementation - you might be able to do better // by for example just copying the bytes from rhs. MyPointerHolder tmp(rhs); std::swap(value, tmp.value); } }; struct MyClass { MyPointerHolder a; MyPointerHolder b; int i; float f; MyClass(int i, float f) : a(12), b(23), i(i) f(f) {} }; 

Now MyClass does not need an explicit constructor constructor, destructor or assignment. By default, everything is in order. If the initializer for b throws, the destructor for MyClass is still not called. But since element a was constructed, its destructor is called and frees memory.

Thus, in addition to getting exception safety, you also reduced your problem of writing one large clear copy by writing code per member of MyClass to write several small explicit copies by typing some code for each pointer type in MyClass, which may be reused in other classes that contain pointers with the same deep copy.

Finally, every time you come to write one of these little helper classes, ask yourself if there is a standard library class that does the same job for you. In my example, there are two strong candidates: std::string and std::vector<char> . There are also various suggested implementations floating around copy_ptr or clone_ptr that might work.

+3
source share

You can not. Just assign members one by one in the initialization list.

+1
source share

If your structure contains more than one pointer, you will run into problems that do it right. Unless you are actually creating a smart pointer, you should wrap all your pointers inside your object (so that you don't actually have pointers).

// Do it without smart pointers. Heavy. Let me have a demo (and I'm probably wrong because it is complicated)

 class D { /* Has a constructor and Destructor */ }; class A { int a; D* b; D* c; public: // Default constructor and destructor are easy A() :a(1) ,b(new D) ,c(new D[10]) {} ~A() { delete b; delete [] c; } // Standard copy and swap for assignment. // Which pushes all the real work into one place A& operator=(A const& copy) { A tmp(copy); tmp.swap(*this); return *this; } void swap(A& s) throws() { std::swap(a,sa); std::swap(b,sb); std::swap(c,sc); } // OK the hard part getting the copy constructor correct A(A const& copy) { a = copy.a; b = new D(copy.b); // may throw but at this point we don' care // if new throws memory is de-allocated. // If a D constructor throws then all fully constructed members are // destroyed before the memory is de-allocated try { c = new D[10]; // If this throws we do care. // As B needs to be deleted to prevent memory leak // So this whole part needs to be put in a try catch block try { // This part needs to be in its own try catch block // If the copy constructor throws then we need to delete c // Note this needs to be seporate to the allocation // As a throw from the call to new that throws will not set c thus calling // delete on it will generate undefined behavior. for(int loop=0;loop < 10;++loop) { std::copy(&copy.c[0],&copy.c[10],c); } } catch(...) { delete [] c; throw; } } catch(...) { delete b; throw; } } }; 

Doing this with just one pointer in a class is simpler, but usually not worth the effort.
The more pointers to your class the more confusing copy constructor contains, the better (for proper operation). As a result, wrap the pointer in the appropriate wrapper that does all the work of getting the code for that pointer. Remember the MAX 1 pointer for each class.

In the above case, the wrapper for the array, such as dynamic allocation, is std :: vector, whereas a single std :: auto_ptr object would work, and we can simplify the above code as:

 class D { /* Has a constructor and Destructor */ }; template<typename T> class DeepCpyAPtr // I am surprised I did not find anything like this in boost { // mybe I am over thinking this and will regret it std::auto_ptr<T> data; public: explicit DeepCpyAPtr(T* d = NULL) :data(d) {} // Other constructors as required DeepCpyAPtr(DeepCpyAPtr const& copy) :data(new D(copy.data.get())){} DeepCpyAPtr& operator=(DeepCpyAPtr const& copy) { DeepCpyAPtr t(copy); t.data.swap(data); return *this; } // Destructor Magical // Add all the methods you need from std::auto_ptr here. T* get() {return data.get();} T& operator*() {return data.operator*();} T* operator->() {return data.operator->();} void reset(T* d = NULL) {data.reset(d);} }; class A { int a; DeepCpyAPtr<D> b; std::vector<D> c; public: // Default constructor is easy A() :a(1) ,b(new D) ,c(10) {} // All the compiler generated versions work perfectly now. }; 

Since std :: auto_ptr does not have the correct symmatics. I actually wrote a wrapper for a single version that uses auto_ptr internally. But this simple one additional class (plus vector) makes the implementation trivial.

+1
source share

This can be done with some indirection ... So I came :)

It is based on the implementation of boost::shared_ptr and can benefit from good acceleration if, instead of holding a pointer to the memory, we actually glue two memory blocks ... but then there are problems with alignment, etc ... so I I won’t do it from the top of my hat.

First, we need a class whose purpose is to manage our memory, if necessary, if necessary, use a custom deallocator.

It is indirect, and that is where the magic is.

Note that it implements deep copy behavior.

 namespace detail { // The interface template <class T> class MemoryOwnerBase { public: virtual ~MemoryOwnerBase() { this->dispose(mItem); mItem = 0; } virtual void dispose(T* item) = 0; virtual void clone() const = 0; T* get() { return mItem; } T* release() { T* tmp = mItem; mItem = 0; return tmp; } void reset(T* item = 0) { if (mItem && item != mItem) this->dispose(mItem); mItem = item; } protected: explicit MemoryOwnerBase(T* i = 0): mItem(i) {} MemoryOwnerBase(const MemoryOwnerBase& rhs): mItem(0) { if (rhs.mItem) mItem = new_clone(*rhs.mItem); // Boost Clonable concept } MemoryOwnerBase& operator=(const MemoryOwnerBase& rhs) { MemoryOwnerBase tmp(rhs); this->swap(rhs); return *this; } private: T* mItem; }; // by default, call delete template <class T> struct DefaultDisposer { void dispose(T* item) { delete item; } }; // the real class, the type of the disposer is erased from the point of view // of its creator template <class T, class D = DefaultDisposer<T> > class MemoryOwner: public MemoryOwnerBase, private D // EBO { public: MemoryOwner(): MemoryOwnerBase(0), D() {} explicit MemoryOwner(T* item): MemoryOwnerBase(item), D() {} MemoryOwner(T* item, D disposer): MemoryOwnerBase(item), D(disposer) {} virtual void dispose(T* item) { ((D&)*this).dispose(item); } virtual MemoryOwner* clone() const { return new MemoryOwner(*this); } }; // easier with type detection template <class T> MemoryOwnerBase<T>* make_owner(T* item) { return new MemoryOwner<T>(item); } template <class T, class D> MemoryOwnerBase<T>* make_owner(T* item, D d) { return new MemoryOwner<T,D>(item,d); } } // namespace detail 

Then we can create our Pimpl class, as it will be yours after.

 template <class T> class Pimpl { typedef detail::MemoryOwnerBase<T> owner_base; public: Pimpl(): mItem(0), mOwner(0) {} explicit Pimpl(T* item): mItem(item), mOwner(item == 0 ? 0 : detail::make_owner(item)) {} template <class D> Pimpl(T* item, D d): mItem(item), mOwner(item == 0 ? 0 : detail::make_owner(item, d)) {} Pimpl(const Pimpl& rhs): mItem(), mOwner() { if (rhs.mOwner) { mOwner = rhs.mOwner.clone(); mItem = mOwner->get(); } } T* get() { return mItem; } const T* get() const { return mItem; } void reset(T* item = 0) { if (item && !mOwner) mOwner = detail::make_owner(item); if (mOwner) { mOwner->reset(item); mItem = mOwner->get(); } } template <class D> void reset(T* item, D d) { if (mOwner) { if (mItem == item) mOwner->release(); delete mOwner; } mOwner = detail::make_owner(item, d); mItem = item; } T* operator->() { return mItem; } const T* operator->() const { return mItem; } T& operator*() { return *mItem; } const T& operator*() const { return *mItem; } private: T* mItem; // Proxy for faster memory access detail::MemoryOwnerBase<T>* mOwner; // Memory owner }; // class Pimpl 

Alright pfiou!

Now you can use it :)

 // myClass.h class MyClass { public: MyClass(); private: struct Impl; Pimpl<Impl> mImpl; }; // myClass.cpp struct MyClass::Impl { Impl(): mA(0), mB(0) {} int mA; int mB; }; // Choice 1 // Easy MyClass::MyClass(): mImpl(new Impl()) {} // Choice 2 // Special purpose allocator (pool ?) struct MyAllocatorDeleter { void dispose(Impl* item) { /* my own routine */ } }; MyClass::MyClass(): mImpl(new Impl(), MyAllocatorDeleter()) {} 

Yes, it's magical;)

The principle is to call Type Erasure . The mechanism ensures that as soon as the MemoryOwner object is built, it knows how to delete the stored memory and hide the exact mechanism from the caller through an indirect direction.

Thus, you can consider the Pimpl<T> object as a value:

  • Semantics of DeepCopy
  • Semantics of DeepConst ( volatile ignored ...)
  • By default, CopyConstructor, Assignment Operator and Destructor are accurate

But be careful that it hides the pointer, and your role should be honored with its non-null before dereferencing.

The code can be greatly simplified if you remove the lazy initialization of the mOwner parameter. In addition, there are some security restrictions for exceptions: the copy constructor for deletion must be non-throw, otherwise all bets are disabled.

EDIT

Explanations.

The problem here is code isolation. A series of operations can be performed on a pointer regardless of the type that it points to, but to create or destroy we need to know the base type.

The creation and destruction and, therefore, knowledge of the basic type are required in four basic principles:

  • Constructor
  • Copy constructor
  • Assignment operator (destruction of the old value)
  • Destructor

which themselves are necessary to obtain the semantics of values.

In C++ there is an idiom called Type Erasure , which consists of information such as embedding behind a virtual interface. And thus, the first part of the design:

 template <class T> class MemoryOwnerBase {}; template <class T, class D> class MemoryOwner: public MemoryOwnerBase<T> {}; 

MemoryOwnerBase provide the basic operations (building, deep copying and destruction) that we are looking for, and hide specific type information (how to delete it correctly).

MemoryOwner implements virtual MemoryOwnerBase methods and encapsulates the knowledge needed to destroy pointers thanks to the D parameter (for deletion).

Now, to manipulate MemoryOwnerBase , we need a pointer / reference to it that has no semantics of values ​​and, thus, we transfer it to the Pimpl class (which denotes a pointer to an implementation) that has the correct semantics of values.

Note that only a deletion device is required for deletion (for destruction), since the user must provide the pointer on his own and, therefore, use the new operator.

The refinement will be to provide the Pimpl<T> make_pimpl<T,D>(const T&, const D&) method, which will see the memory allocation, etc .... but I have not received this yet due to the above storage alignment issues .

+1
source share

I would declare dynamically allocated elements by structure, and then memcpy () them;

 #define _WIN32_WINNT 0x0400 #define WIN32_LEAN_AND_MEAN #include <windows.h> class MyClass { private: static int some_static_member; public: struct DynamicallyAllocated { int x, y, z; } *dyn; public: MyClass(MyClass* copy = NULL) { this->dyn = NULL; if(copy != NULL) { if(copy->dyn != NULL) { this->dyn = new DynamicallyAllocated(); memcpy(this->dyn, copy->dyn, sizeof(*this->dyn)); } } } }; int MyClass::some_static_member = 0; void main() { MyClass mc1(NULL); mc1.dyn = new MyClass::DynamicallyAllocated(); mc1.dyn->x = 1; mc1.dyn->y = 2; mc1.dyn->z = 3; MyClass mc2(&mc1); } 

You need to β€œgroup” the members within the structure, so when using memcpy (), you will not overwrite some other C ++ data with C ++, such as pointers to virtual functions.

0
source share

All Articles