Copy Control Part I Copy control consists of








- Slides: 8
Copy Control (Part I) • Copy control consists of 5 distinct operations – A copy constructor initializes an object by duplicating the const l-value that was passed to it by reference – A copy-assignment operator (re)sets an object’s value by duplicating the const l-value passed to it by reference – A destructor manages the destruction of an object – A move constructor initializes an object by transferring the implementation from the r-value reference passed to it – A move-assignment operator (re)sets an object’s value by transferring the implementation from the r-value reference passed to it • Today we’ll focus on the first 3 operations and will defer the others (introduced in C++11) until next time – The others depend on the new C++11 move semantics CSE 332: C++ copy control I
Basic Copy Control Operations • A copy constructor or copy-assignment operator takes a reference to a (usually const) instance of the class – Copy constructor initializes a new object from it – Copy-assignment operator sets object’s value from it – In either case, original the object is left unchanged (which differs from the move versions of these operations) – Destructor takes no arguments (except implicit this) • Copy control operations for built-in types – Copy construction and copy-assignment copy values – Destructor of built-in types does nothing (is a “no-op”) • Compiler-synthesized copy control operations – Just call that same operation on each member of the object – Uses defined/synthesized definition of that operation for user -defined types (see above for built-in types) CSE 332: C++ copy control I
Preventing or Allowing Basic Copy Control • Old (C++03) way to prevent compiler from generating a default constructor, copy constructor, destructor, or assignment operator was somewhat awkward – Declare private, don’t define, don’t use within class – This works, but gives cryptic linker error if operation is used • New (C++11) way to prevent calls to any method – End the declaration with = delete (and don’t define) – Compiler will then give an intelligible error if a call is made • C++11 allows a constructor to call peer constructors – Allows re-use of implementation (through delegation) – Object is fully constructed once any constructor finishes • C++11 lets you ask compiler to synthesize operations – Explicitly, but only for basic copy control, default constructor – End the declaration with = default (and don’t define) CSE 332: C++ copy control I
Shallow Copy Construction // just uses the array that’s already in the other object Int. Array: : Int. Array(const Int. Array &a) : size_(a. size_), values_(a. values_) { } • There are two ways to “copy” – Shallow: re-aliases existing resources • E. g. , by copying the address value from a pointer member variable – Deep: makes a complete and separate copy • I. e. , by following pointers and deep copying what they alias • Version above shows shallow copy – Efficient but may be risky (why? ) – Usually want no-op destructor, aliasing via shared_ptr CSE 332: C++ copy control I
Deep Copy Construction // makes its own copy of the array Int. Array: : Int. Array(const Int. Array &a) : size_(0), values_(nullptr) { • This code shows deep copy if (a. size_ > 0) { // new may throw bad_alloc, // set size_ after it succeeds values_ = new int[a. size_]; size_ = a. size_; – Safe: no shared aliasing, exception aware initialization – But may not be as efficient as shallow copy in many cases // could use memcpy instead for (size_t i = 0; i < size_; ++i) { values_[i] = a. values_[i]; } – Allocate memory once – More efficient than multiple calls to new (heap search) – Constructor and assignment called on each array element – Less efficient than block copy } } • Note trade-offs with arrays • E. g. , using memcpy() – But sometimes necessary • i. e. , constructors, destructors establish needed invariants CSE 332: C++ copy control I
Swap Trick for Copy-Assignment • Cleanup/assignment succeed or fail together class Array { public: Array(unsigned int) ; // assume constructor allocates memory Array(const Array &); // assume copy constructor makes a deep copy ~Array(); // assume destructor calls delete on values_ Array & operator=(const Array &a); private: size_t size_; int * values_; }; Array & Array: : operator=(const Array &a) { // return ref lets us chain if (&a != this) { // note test for self-assignment (safe, efficient) Array temp(a); // copy constructor makes deep copy of a swap(temp. size_, size_); // note unqualified calls to swap(temp. values_, values_); // (do user-defined or std: : swap) } return *this; // previous *values_ cleaned up by temp’s destructor } CSE 332: C++ copy control I
Constructors and Destructors are Inverses Int. Array: : Int. Array(unsigned int u) : size_(0), values_(nullptr) { // exception safe semantics values_ = new int [u]; size_ = u; } • Constructors initialize Int. Array: : ~Int. Array() { • Destructors clean up // deallocates heap memory // that values_ points to, // so it’s not leaked: // with deep copy, object // owns the memory delete [] values_; // // the size_ and values_ member variables are themselves destroyed after destructor body } CSE 332: C++ copy control I – At the start of each object’s lifetime – implicitly called when object is created – Implicitly called when an object is destroyed • E. g. , when stack frame where it was declared goes out of scope • E. g. , when its address is passed to delete • E. g. , when another object of which it is a member is being destroyed
More on Initialization and Destruction • Initialization follows a well defined order – Base class constructor is called • That constructor recursively follows this order, too – Member constructors are called • In order members were declared • Good style to list in that order (a good compiler may warn if not) – Constructor body is run • Destruction occurs in the reverse order – Destructor body is run, then member destructors, then base class destructor (which recursively follows reverse order) • Make destructor virtual if members are virtual – Or if class is part of an inheritance hierarchy – Avoids “slicing”: ensures destruction starts at the most derived class destructor (not at some higher base class) CSE 332: C++ copy control I