Chapter 13. Copy Control
A class controls these operations by defining five special member functions: copy constructor, copy-assignment operator, move constructor, move-assignment operator, and destructor. The copy and move constructors define what happens when an object is initialised from another object of the same type. The copy- and move-assignment operators define what happens when we assign an object of a class type to another object of that same class type. The destructor defines what happens when an object of the type ceases to exist. Collectively, we will refer to these operations as copy control.
13.1 Copy, Assign, and Destroy
13.1.1 The Copy Constructor
A constructor is the copy constructor if its first parameter is a reference to the class type and any additional parameters have default values:
The copy constructor usually should not be explicit.
When we do not define a copy constructor for a class, the compiler synthesises one for us. Unlike the synthesised default constructor, a copy constructor is synthesised even if we define other constructors.
The synthesised copy constructor for some classes prevents us from copying objects of that class type.
The compiler copies each non-static member in turn from the given object into the one being created.
When we use copy initialisation, we are asking the compiler to copy the right-hand operand into the object being created, converting that operand if necessary.
Copy initialisation ordinarily uses the copy constructor. However, if a class has a move constructor, then copy initialisation sometime uses the move constructor instead of the copy constructor.
Copy initialisation happens not only when we define variables using an =, but also when we:
Pass an object as an argument to a parameter of non-reference type.
Return an object from a function that has a non-reference return type.
Brace initialise the elements in an array or the members of an aggregate class.
13.1.2 The Copy-assignment Operator
As with the copy constructor, the compiler synthesises a copy-assignment operator if the class does not define its own.
The copy-assignment operator takes an argument of the same type as the class:
Assignment operators ordinarily should return a reference to their left-hand operand.
13.1.3 The Destructor
The destructor do whatever work is needed to free the resources used by an object and destroy the non-static data members of the object.
Because it takes no parameter, it cannot be overloaded.
The implicit destruction of a member of built-in pointer type does not delete the object to which that pointer points. The members that are smart pointers are automatically destroyed during the destruction phase.
The destructor is used automatically whenever an object of its type is destroyed:
Variables are destroyed when they go out of scope.
Members of an object are destroyed when the object of which they are a part is destroyed.
Elements in a container, whether a library container or an array, are destroyed when the container is destroyed.
Dynamically allocated objects are destroyed when the delete operator is applied to a pointer to the object.
Temporary objects are destroyed at the end of the full expression in which the temporary was created.
The destructor is not run when are reference or a pointer to an object goes out of scope.
The compiler defines a synthesised destructor for any class that does not define its own destructor.
It is important to realise that the destructor body does not directly destroy the members themselves. Members are destroyed as part of the implicit destruction phase that follows the destructor body. A destructor body executes in addition to the member-wise destruction that takes place as part of destroying an object.
13.1.4 The Rule of Three/Five
One rule of thumb to use when you decide whether a class needs to define its own versions of the copy-control members is to decide first whether the class needs a destructor. Often, the need for a destructor is more obvious than the need for the copy constructor or assignment operator. If the class needs a destructor, it almost surely needs a copy constructor and copy-assignment operators as well.
A second rule of thumb: If a class needs a copy constructor, it almost surely needs a copy-assignment operator, and vice versa.
13.1.5 Using = default
We can explicitly ask the compiler to generate the synthesised versions of the copy-control members by defining them as = default
.
13.1.6 Preventing Copies
Most classes should define, either implicitly or explicitly, the default and copy constructors and the copy-assignment operator.
Under the new standard, we can prevent copies by defining the copy constructor and copy-assignment operator as deleted functions.
The = delete
signals to the compiler that we are intentionally not defining these members.
Unlike = default
, = delete
must appear on the first declaration of a deleted function. This difference follows logically from the meaning of these declarations. A defaulted member affects only what code the compiler generates; hence the = default
is not needed until the compiler generates code. On the other hand, the compiler needs to know that a function is deleted in order to prohibit operations that attempt to use it.
Also unlike = default, we can specify = delete
on any function (we can use = default
only on the default constructor or a copy-control member that the compiler can synthesise).
If a member has a deleted destructor, then that member cannot be destroyed. If a member can't be destroyed, the object as a whole can't be destroyed.
For some classes, the compiler defines these synthesised members as deleted functions:
The synthesised destructor is defined as deleted if the class has a member whose own destructor is deleted or is inaccessible (e.g., private).
The synthesised copy constructor is defined as deleted if the class has member whose own copy constructor is deleted or inaccessible. It is also deleted if the class has a member with a deleted or inaccessible destructor.
The synthesised copy-assignment operator is defined as deleted if a member has a deleted or inaccessible copy-assignment operator, or if the class has a const or reference member.
The synthesised default constructor is defined as deleted if the class has a member with a deleted or inaccessible destructor; or has a reference member that does not have an in-class initialiser; or has a const member whose type does not explicitly define a default constructor and that member does not have an in-class initialiser.
Prior to the new standard, classes prevented copies by declaring their copy constructor and copy-assignment as private.
Classes that want to prevent copying should define their copy constructor and copy-assignment operators using = delete
rather than making those members private.
13.2 Copy Control and Resource Management
13.2.1 Classes That Act Like Value
to implement value-like behaviour class needs:
A copy constructor that copies the data, not just the pointer.
A destructor to free the data.
A copy-assignment operator to free the object's existing data and copy the data from its right-hand operand.
13.2.2 Defining Classes That Act Like Points
The easiest way to make a class act like a pointer is to use shared_ptr
to manage the resources in the class.
Sometime, we want to manage a resource directly. In such cases, it can be useful to use a reference count. Reference counting works as follows:
In addition to initialising the object, each constructor creates a counter. This counter will keep track of how many objects share state with the object we are creating. When we create an object, there is only one such object, so we initialise the counter to 1.
the copy constructor does not allocate a new counter; instead, it copies the data members of its given object, including the counter. the copy constructor increments this shared counter, indicating that there is another user of that object's state.
The destructor decrements the counter, indicating that there is one less user of the shared state. If the count goes to zero, the destructor deletes that state.
The copy-assignment operator increments the right-hand operand's counter and decrements the counter of the left-hand operand.
The counter usually store in dynamic memory.
13.3 Swap
Unlike the copy-control members, swap is never necessary. However, defining swap can be an important optimisation for classes that allocate resources.
Classes that define swap often use swap to define their assignment operator. These operators use a technique known as copy and swap.
The interesting thing about this technique is that it automatically handles self assignment and is automatically exception safe.
Assignment operators that use copy and swap are automatically exception safe and correctly handle self-assignment.
13.4 Classes That Manage Dynamic Memory
The classes will have three pointers into the space it uses for its elements:
elements
, which points to the first element in the allocated memory.first_free
, which points just after the last actual element.cap
, which points just past the end of the allocated memory.
and it will also have four utility functions:
alloc_n_copy
will allocate space and copy a given range of elements.free
will destroy the constructed elements and deallocate the space.chk_n_alloc
will ensure that there is room to add at least one more element to the class.reallocate
will reallocate the class when it runs out of space.
The class body defines several of tis members:
The default
constructor
default initialises alloc and initialises the pointers to nullptr, indicating that there are no elements.The
size
member returns the number of elements actually in use, which is equal tofirst_free - elements
.The capacity member returns the number of elements that the class can hold, which is equal to
cap - elements
.The
chk_n_alloc
causes the class to be reallocated when there is no room to add another element, which happens whencap == first_free
.The
begin
andend
members return pointers to the first and one past the last constructed element, respectively.
The push_back function:
The alloc_n_copy
member is called when we copy or assign a StrVec
:
The free
member has two responsibilities: It must destroy the elements and then deallocate the space that this StrVec
itself allocated:
The copy-control members:
The reallocate member will:
allocate memory for a new, larger array of data.
construct the first part of that space to hold the existing elements.
destroy the elements in the existing memory and deallocate that memory.
13.5 Moving Objects
In some of these circumstances, an object is immediately destroyed after it is copied. A second reason to move rather than copy occurs in classes such as the IO or unique_ptr
classes. These classes have a resource that may not be shared.
13.5.1 Rvalue References
To support move operations, the new standard introduced a new kind of reference, an rvalue reference. An rvalue reference is a reference that must be bound to an rvalue. An rvalue reference is obtained by using &&
rather than &
. As a result, we are free to "move" resources from an rvalue reference to another object.
An lvalue expression refers to an object's identity whereas an rvalue expression refers to an object's value.
We can also obtain an rvalue reference bound to an lvalue by calling a new library function named move, which is defined in the utility
header.
13.5.2 Move Constructor and Move Assignment
Move constructors and move assignment operators that cannot throw exceptions should be marked as noexcept
.
The move-assignment operator:
After a move operation, the "moved-from" object must remain a valid, destructible object but users may make no assumptions about its value.
The compiler synthesises the move constructor and move assignment only if a class does not define any of its own copy-control members and only if all the data members can be moved constructed and move assigned, respectively.
If we explicitly ask the compiler to generate a move operation by using =default
, and the compiler is unable to move all the members, then the move operation will be defined as deleted. With one important exception, the rules for when a synthesised move operation is defined as deleted are analogous to those for the copy operations:
Unlike the copy constructor, the move constructor is defined as deleted if the class has a member that defines tis own copy constructor but does not also define a move constructor, or if the class has a member that doesn't define its own coy operations and for which the compiler is unable to synthesise a move constructor.
The move constructor or move-assignment operator is defined as deleted if the class has a member whose own move constructor or move-assignment operator is deleted or inaccessible.
Like the copy constructor, the move constructor is defined as deleted if the destructor is deleted or inaccessible.
Like the copy-assignment operator, the move-assignment operator is defined as deleted if the class has a
const
or reference member.
When a class has both a move constructor and a copy constructor, the compiler uses ordinary function matching to determine which constructor to use.
If a class has a usable copy constructor and no move constructor, objects will be "moved" by the copy constructor. Similarly for the copy-assignment operator and move-assignment.
If we add a move constructor to this class, it will effectively get a move assignment operator as well:
The new library defines a move iterator adaptor. A move iterator adapts its given iterator by changing the behaviour of the iterator's dereference operator. The dereference operator of a move iterator yields an rvalue reference.
13.5.3 Rvalue References and Member Function
Overloaded functions that distinguish between moving and copying a parameter typically have one version that takes a const T&
and one that takes a T&&
.
Just as we can overloaded a member function based on whether it is const, we can also overload a function based on its reference qualifier.
If a member function has a reference qualifier, all the versions of that member with the same parameter list must have reference qualifiers.
Last updated
Was this helpful?