Concepts / Copy and Move Operations

Copy and Move Operations

The central idea is that copying is not automatically correct for every type.

  • Programming

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?

  • Yes, it always generates one

  • No, it does not generate an ordinary constructor

Reveal answer
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.

Definition

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.

source objectowns the resourceheap resourceowned by sourcesource objectemptyheap resourceowned by destinationdestination objectno resource yetdestination objectowns the resourceownsowns
After a move, who owns the heap resource, and what state is the source object left in, compared with before the move?

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.

object apointer memberheap resourceone allocationobject bpointer copied by defaultexternal memoryowned elsewhereobject cpointer to external memoryobject dpointer copied by defaultpoints topoints to samepoints topoints to same
When the default copy duplicates a pointer, do both objects point to the same memory, and what happens when each destructor runs?

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.

class with membersdestructor declaredcopy and move needreviewdefine or delete themcompiler generatespointer or referencememberdecide ownershipdocument external resourcesredefine or prohibitdefault copyappropriateyesnoyesnoyesno
Given the class members and user-declared functions, what decision steps determine whether each special member function is generated, deleted, or must be user-defined?

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.

AspectPassing by valuePassing by const reference
What is copied at the call siteThe argument is copied into the parameterNo copy of the argument is made for the parameter
Can the function modify the caller's objectOnly its own copy is changedNo, the const reference prevents modification
Suitable for large operandsCostly, because the full object is copied on the way inSuitable, because the function reads the object without copying it
Source's guidanceUsed for returning containers, where move is efficientRecommended for large operands taken as input

Two ways a large object can cross a function boundary

Worked Decision: A Class That Owns a Buffer

Worked example

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?

  1. 1
    Identify the member

    The member is a pointer, so the source asks for copy and move behavior to be considered in light of ownership.

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

  3. 3
    Apply the destructor rule

    A class with a destructor probably needs user-defined or deleted copy and move operations.

  4. 4
    Check 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.

  5. 5
    Choose 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.

Result

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 fails

    The source states the compiler does not generate an ordinary constructor for you, though it can generate other special member functions as needed.

    Do this instead

    Write the constructors your class needs explicitly.

  • Common mistake

    Writing a destructor and leaving copy and move unconsidered

    What it looks like

    A class with a destructor that releases a pointer resource is copied with the default copy, so two objects share one resource.

    Why it fails

    A class with a destructor probably needs user-defined or deleted copy and move operations.

    Do this instead

    Define copy and move so that ownership is correct, or delete them.

  • Common mistake

    Treating a pointer member as owned without checking

    Why it fails

    A 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 instead

    Decide 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 fails

    Explicitly defining some defaults prevents other defaults from being generated, so the whole set changes.

    Do this instead

    After 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

MEDIUM

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.

Related concepts

More concepts starting with C