Mastering Object Lifecycle Operations
An object is copied or moved in five situations: as the source of an assignment, as an object initializer, as a function argument, as a function return value, and as an exception.
Values That Travel Quietly
When you write a program with objects, a value often travels from one place to another. An object may be assigned to another object, handed to a function, sent back out of a function, or thrown as part of an error. Each of these moments can cause a new object to be created from an existing one. Because these events happen quietly, they are easy to overlook, yet they determine which constructor runs and what work it does.
This article approaches the topic from the perspective of the program's events. Rather than starting with the constructors themselves, we first ask a practical question: in which moments does an object get copied or moved? Once that is clear, we can ask which constructor takes over in each case. Think of this as learning to read the program the way the language reads it, one event at a time.
Tracing One Value Through a Program
Before naming any rules, follow a single object through a small, original scenario. The scenario is an illustration written for this article and is not taken from the source material. Suppose a program creates an object called order, passes it to a function that checks it, stores a second copy in a list, and then returns a summary object built from it. Each step below asks what happened to the object at that moment.
- 1Step 1: the object order is created and given its initial contents. This is initialization of a named object.
- 2Step 2: order is assigned to another existing object. The receiving object already exists, so this is an assignment, and the value is copied or moved into it.
- 3Step 3: order is handed to a checking function by value. The parameter inside the function is a new object built from order, so a copy happens at the call.
- 4Step 4: the function returns a summary object built from order. The returned value leaves the function as a new object, which is a function return value situation.
- 5Step 5: if the checking function had thrown order as an error, the thrown object would also be a copy or move of order.
The Five Situations
An object is copied or moved in five situations. Read the list below as a checklist of places in code where a copy or move can happen. The order is not a sequence of steps; it is a set of distinct moments that any program might contain several of at once.
- As the source of an assignment: an existing object is the value being given to another object.
- As an object initializer: a new object is created and its starting value comes from another object.
- As a function argument: the object is passed into a function, which receives its own object.
- As a function return value: an object leaves a function as the function's result.
- As an exception: an object is thrown as part of an error.
A single program can involve several of these situations at once, as the tracing scenario above showed. The goal is not to memorize a sequence but to be able to point to each event in the code and name which of the five it is.
Assignment Versus Initialization
The first situation (assignment) and the second (initialization) are easy to confuse, because both place a value into an object. The difference lies in the state of the receiving object. Initialization describes how an object is being created. Assignment describes an object that already exists receiving a value. Both can involve an object being copied or moved, which is exactly why the list names them separately.
Which Constructor Takes Over
Whenever an object is copied or moved, the copy or move constructor is the one applied. This is the central link between the five situations and the constructors that handle them. The constructor is what carries out the work of producing the new object, so knowing which one runs tells you what work is being done.
The source names the copy constructor and the move constructor but does not spell out the full rule for choosing between them. In the terminology this course uses, a named object is an lvalue and a temporary object is an rvalue, and that difference is the usual basis for the choice. The comparison below shows the typical pairing; treat it as a guide to what to check in code rather than a complete specification.
Why does this matter? The two constructors do different work. Choosing the correct one means the new object is produced the way the language intends for that kind of source, so reading which constructor applies helps you predict what the program is actually doing when a value moves.
Other Jobs Constructors Perform
Copying and moving are not the only work constructors do. Constructors also initialize named objects and objects on the free store. In addition, they are used to initialize temporary objects and to implement explicit type conversion. Keeping these roles separate in your mind prevents a common confusion: not every constructor call is a copy or move.
- Initializing named objects: giving a named object its starting state when it is created.
- Initializing objects on the free store: creating objects in the free store and setting up their starting state.
- Initializing temporary objects: creating unnamed objects that exist only for part of an expression.
- Implementing explicit type conversion: a constructor can convert a value of one type into an object of another type when the conversion is requested.
The copy or move constructor is the one tied to the five situations. The other entries describe initialization work that constructors also perform. When a temporary object is created during an expression, that temporary is a value the surrounding expression uses and then no longer needs under its own name.
Tracing a Returned Object
Following a local object out of a function
A function builds a local object named report, fills it in, and returns it to the caller, which stores the result in a variable. Identify each copy or move event and the constructor it involves, and note where the source's optimization caveat applies.
- 1Create the local object
report is created inside the function. This is initialization of a named object, so a constructor runs for it.
- 2Return it
Returning report is the function return value situation. The return value is an object leaving the function, so the copy or move constructor is the one applied, unless it can be optimized away.
- 3Store it in the caller
The caller's variable receives the returned value. If that receipt is an initialization of a new object, it is an object initializer situation. If the variable already existed, it is an assignment situation. Read the code to decide which.
- 4Apply the optimization check
Ask whether the copy or move in the return can be optimized away. If so, the constructor call does not need to happen in the final program. The source does not describe which optimizations apply, so check rather than assume.
The returned object involves the function return value situation and, depending on how the caller stores it, possibly an initializer or assignment situation. Each event is governed by the copy or move constructor, except where the optimization removes the call.
The lesson of the example is that one line of code can hide more than one event. Reading returns and stores separately, and asking which situation each belongs to, keeps you from missing the constructor calls they imply.
When the Copy Is Optimized Away
The source states that the copy or move constructor is applied in all five situations, with one qualification: unless it can be optimized away. This phrase matters. It means that a situation which looks like it should produce a copy or move does not always produce a constructor call in the final program.
Mistakes Beginners Make
Common mistake Treating assignment and initialization as the same event
Why it failsBoth place a value into an object, but initialization creates a new object while assignment gives a value to one that already exists.
Do this insteadAsk whether the receiving object is new or already exists before naming the situation.
Common mistake Assuming that a constructor call always means a copy or move
Why it failsConstructors also initialize named objects, free-store objects, temporary objects, and perform explicit type conversion. Only the copy or move constructor is tied to the five situations.
Do this insteadIdentify which job the constructor is doing before deciding whether the event is a copy or move.
Common mistake Overlooking the events in a return or a thrown exception
Why it failsThese events are quiet and easy to miss, but they still determine which constructor runs.
Do this insteadCheck every return and every thrown exception as a checklist item.
Common mistake Believing a copy or move will always happen because the code looks like one. The source says the call may be optimized away, so the constructor call is not guaranteed.
Common mistake Predicting the optimization from memory. The source does not describe the particular optimizations, so read the code and treat optimization as a case to verify.
Practice Tracing Object Events
Read this description of a program: an object config is created with starting values; config is then assigned to an existing object named backup; config is passed by value to a function called validate; validate returns a result object built from config; and a temporary object is created during a conversion expression. List every copy or move event you find, name the situation for each from the five, and state which constructor is applied or whether the call might be optimized away.
Hints
- Start with the events that involve config, then look for the temporary object separately.
- The creation of config is an initialization of a named object, which is a constructor job but not necessarily a copy or move.
- Ask of each remaining event whether the receiving object already exists, since that separates assignment from initialization.
Choose one function in a program you have written. Mark every place where an object is passed in, returned, assigned, initialized from another object, or thrown. For each mark, write which of the five situations it belongs to, and note whether the event is a copy or move or simply another constructor job.
Hints
- Check parameters, return statements, and any line that stores one object into another.
Key Takeaways
- An object is copied or moved in five situations: as the source of an assignment, as an object initializer, as a function argument, as a function return value, and as an exception.
- Whenever an object is copied or moved, the copy or move constructor is the one applied; constructors also initialize named objects, free-store objects, and temporary objects, and implement explicit type conversion.
- Assignment and initialization both place a value into an object, but initialization creates a new object while assignment gives a value to an existing one.
- A copy or move constructor call may be optimized away, so treat optimization as an exception to check for in code rather than a detail you can predict.