1 / 8

Copy Control (Part I)

This article discusses the basic copy control operations in C++. It covers the copy constructor, copy-assignment operator, and destructor. It also explains how to prevent or allow these operations and introduces the concept of shallow and deep copy. The article provides code examples and trade-offs between different techniques.

hobbsm
Download Presentation

Copy Control (Part I)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 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

  2. 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)

  3. 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)

  4. 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 // just uses the array that’s already in the other object IntArray::IntArray(const IntArray &a) :size_(a.size_), values_(a.values_) { } Shallow Copy Construction

  5. This code shows deep copy Safe: no shared aliasing, exception aware initialization But may not be as efficient as shallow copy in many cases Note trade-offs with arrays 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 E.g., using memcpy() But sometimes necessary i.e., constructors, destructors establish needed invariants // makes its own copy of the array IntArray::IntArray(const IntArray &a) :size_(0), values_(nullptr) { if (a.size_ > 0) { // new may throw bad_alloc, // set size_ after it succeeds values_ = new int[a.size_]; size_ = a.size_; // could use memcpy instead for (size_t i = 0; i < size_; ++i) { values_[i] = a.values_[i]; } } } Deep Copy Construction

  6. 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 swap(temp.values_, values_); // (do user-defined or std::swap) } return *this; // previous *values_ cleaned up by temp’s destructor }

  7. Constructors and Destructors are Inverses • Constructors initialize • At the start of each object’s lifetime • implicitly called when object is created • Destructors clean up • 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 IntArray::IntArray(unsigned int u) : size_(0), values_(nullptr) { // exception safe semantics values_ = new int [u]; size_ = u; } IntArray::~IntArray() { // 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 }

  8. 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)

More Related