Copy and Move Operations
The central idea is that copying is not automatically correct for every type.
Why Copying Needs a Decision
When you write a class, you decide what happens when an object is copied, moved, created, and destroyed. These operations are the special member functions of the class. Each time a value is passed around in a program, one of these operations may run, often without the programmer noticing. Copying and moving are the two operations this article focuses on, and they fit together with construction and destruction.
The central idea is that copying is not automatically correct for every type. A class author has to decide whether the default copying behavior suits the type, and if it does not, change it. That decision drives the rest of this article: prohibiting or redefining copying, returning containers by value, and designing the constructors and destructor as a set.
What do you think happens?
If you write a class and give it no constructors at all, does the compiler generate an ordinary constructor for you?
Reveal answer
No, it does not generate an ordinary constructor for you.
The compiler can generate default implementations for special member functions as needed, but an ordinary constructor is the exception. The practical question is whether a needed special member function has been explicitly defined.
The Six Special Member Functions
The special member functions of a class are the default constructor, the destructor, the copy constructor, the copy assignment operator, the move constructor, and the move assignment operator. These are the operations that control how an object is created, duplicated, transferred, and cleaned up.
A class may have several special member functions, and you do not always have to write their implementations yourself. The compiler can generate implementations as needed. The one important exception is the ordinary constructor: the compiler does not generate it for you. So a class that needs to be created in some specific way must have that constructor written by you.
Special member functions: the functions a class can have that govern creation (constructors), duplication (copy operations), transfer (move operations), and cleanup (the destructor). The compiler can generate default implementations of these as needed, except that it does not generate an ordinary constructor.
Tracing What a Move Leaves Behind
A move operation is the mechanism for handing over the contents of an object instead of copying them in full. The source describes this for returned containers: the move operation is what allows the contents of the returned object to be handed to the caller efficiently, rather than copied. Think of a move as a change of ownership of a resource. The resource that was held by one object is now held by another, and the first object no longer has it.
Read the diagram from top to bottom. Before the move, the source object owns the heap resource and the destination has nothing. After the move, the resource belongs to the destination, and the source is left empty. Because the resource was transferred rather than duplicated, there is only one owner at any time. That single-owner outcome is what makes a move efficient, and it is also why the source object must be treated as having no resource after it has been moved from.
Pointer Members and Ownership
For pointer or reference members, the source asks you to consider copy and move behavior in light of ownership, and to document externally owned resources. A pointer member can mean two very different things. It can point to a resource that the class is responsible for releasing, or it can point to memory that some other part of the program owns. The right copy and move behavior depends on which of these is true.
The diagram shows what a default copy does to a pointer. The default copy duplicates the pointer value, not the thing it points to. In both groups, two objects now refer to one memory location. The question of what happens next depends on who is responsible for that memory. The two groups must be handled differently, as the next two subsections explain.
Deciding When Default Copying Fails
Default copying is the compiler's generated behavior for copy and move. It is appropriate when it gives the type the meaning its author intends. When it does not, the author must redefine the operation or prohibit copying altogether. The decision is not whether a class has a member function, but whether a needed special member function has been explicitly defined, and whether the default version is correct for this type.
Read the flowchart as a checklist for one class. Start with the members and any functions you have declared. If a destructor is declared, copy and move need review. If the class has pointer or reference members, decide who owns the referenced resource and document externally owned resources. Only when the default behavior matches what the type means can you let the compiler generate it. Otherwise you define the operation yourself or prohibit it.
How One Definition Changes the Rest
Explicitly defining some default member functions prevents other default definitions from being generated. This is why you cannot treat each special member function as independent. Writing one of them changes what the compiler will supply for the others, so you must check the whole set after each change.
The practical consequence is that constructors, assignments, and the destructor should be designed as a matched set. These functions should agree about how an object is created, duplicated, transferred, and cleaned up. If one of them behaves differently from the others, the class becomes harder to reason about. When you define one, ask what the compiler will now generate for the rest, and whether those generated versions still match the design.
Returning Containers and Taking Large Arguments
A function can return a container by value. Instead of forcing the caller to pass in an output object, the function builds its result and returns it. Returning by value relies on move operations for efficiency: the contents of the returned object are handed to the caller rather than copied in full. The source's advice is to return containers by value and rely on move for efficiency.
The complementary advice concerns inputs. When an operand is large, the source recommends using const reference argument types. A const reference lets a function read a large object without making its own copy of it. In short: return by value when moving is efficient, and accept large inputs by const reference so they are not copied on the way in.
| Aspect | Passing by value | Passing by const reference |
|---|---|---|
| What is copied at the call site | The argument is copied into the parameter | No copy of the argument is made for the parameter |
| Can the function modify the caller's object | Only its own copy is changed | No, the const reference prevents modification |
| Suitable for large operands | Costly, because the full object is copied on the way in | Suitable, because the function reads the object without copying it |
| Source's guidance | Used for returning containers, where move is efficient | Recommended for large operands taken as input |
Two ways a large object can cross a function boundary
Worked Decision: A Class That Owns a Buffer
Classifying a pointer member that the class deletes
A class stores a pointer to a heap buffer. Its destructor deletes that buffer. A programmer leaves copy and move unwritten and expects the compiler's defaults to be fine. Should the defaults be accepted?
- 1Identify the member
The member is a pointer, so the source asks for copy and move behavior to be considered in light of ownership.
- 2Decide who owns the resource
The destructor deletes the buffer, so the class owns the resource. This is a pointer to a resource the class must delete, not a pointer to externally owned memory.
- 3Apply the destructor rule
A class with a destructor probably needs user-defined or deleted copy and move operations.
- 4Check the default outcome
A default copy duplicates the pointer. Two objects would then each run a destructor that deletes the same buffer, so the default is not appropriate.
- 5Choose a fix
Either define copy and move so each object gets correct ownership, or prohibit copying. Both choices are consistent with the source's guidance to redefine or prohibit copying when the default is wrong.
The defaults should not be accepted. The class must redefine or prohibit copying, and the copy and move operations should be designed together with the constructors and destructor.
Mistakes That Break the Matched Set
Common mistake Assuming the compiler generates an ordinary constructor
Why it failsThe source states the compiler does not generate an ordinary constructor for you, though it can generate other special member functions as needed.
Do this insteadWrite the constructors your class needs explicitly.
Common mistake Writing a destructor and leaving copy and move unconsidered
What it looks likeA class with a destructor that releases a pointer resource is copied with the default copy, so two objects share one resource.
Why it failsA class with a destructor probably needs user-defined or deleted copy and move operations.
Do this insteadDefine copy and move so that ownership is correct, or delete them.
Common mistake Treating a pointer member as owned without checking
Why it failsA pointer may refer to memory that another part of the program owns. Deleting it in the class's destructor would be wrong in that case.
Do this insteadDecide whether the class owns the pointed-to resource, and document externally owned resources.
Common mistake Defining one special member function and forgetting the others
Why it failsExplicitly defining some defaults prevents other defaults from being generated, so the whole set changes.
Do this insteadAfter any change, check every special member function and make sure the set agrees.
Common mistake Passing large operands by value when a const reference would avoid the copy
Practice: Classify the Members
A class has three members: an integer count, a pointer to a heap array that the class's destructor deletes, and a pointer to a configuration object owned by another part of the program. For each member, state whether default copying is safe, and say what the class should do about copy and move.
Hints
- The integer count is not a pointer, so ownership does not come into question for it.
- Ask which pointer the destructor releases and which one it must not release.
- A class with a destructor probably needs user-defined or deleted copy and move operations.
Key Takeaways
- The compiler can generate default implementations of the special member functions as needed, but it does not generate an ordinary constructor for you.
- Copying is not automatically correct for every type. Pointer and reference members require explicit decisions about ownership, and externally owned resources should be documented.
- Explicitly defining some default functions prevents other defaults from being generated, so the constructors, assignments, and destructor must be designed as a matched set.
- A class with a destructor probably needs user-defined or deleted copy and move operations. Otherwise default copying can leave two objects managing one resource.
- Return containers by value and rely on move for efficiency, and pass large operands as const references so they are not copied on the way in.