C++ Copying Objects and the Rule of Zero
Understand C++ value semantics and the Rule of Zero with independent string/vector copies, copy initialization versus assignment, lifetime boundaries and a record-copy lab.
What you will learn
- Explain independent copies for a record owning string and vector members.
- Distinguish copy construction from copy assignment.
- Recognize aliasing through a reference as different from copying a value.
- Apply the Rule of Zero without confusing resource safety with domain validity.
- Test empty and nonempty copied collections without invalid access.
A value copy represents another independently editable record
A record containing a std::string title and a std::vector<int> can usually use the compiler-generated copy operations. Copying that record copies each member according to the member's own semantics. A copied string owns its text value and a copied vector owns its integer sequence. Editing those members in the copy does not edit the original record's corresponding members. That useful behavior comes from the chosen member types, not merely the word struct.
This is value semantics in a concrete example: the new object initially represents the same data but has its own lifetime and can change independently. It does not require all values to occupy identical addresses or produce a particular number of allocations. Small-string optimization and allocation strategies are implementation details. Test observable title and element values rather than printing addresses and treating an accidental layout as the definition of correctness.
Contrast auto copy = original with auto& alias = original. The first creates another object from the original value; the second creates a reference to the existing object. A write through alias changes that existing object because no independent record was created. Both forms are useful when intentional. The name copy does not create independence if its declaration actually uses a reference, so read the type and initializer rather than trusting the variable's label.

Change a copied record without changing its source
The copied vector receives another integer and the copied string receives another title. No writable alias is returned or stored.
Predict the output, edit the source, then select Run code.
Compare with the starter's expected output
Morning: 2
Revised: 3
After editing inputs, calculate a new prediction; this reference describes only the starter.
Copy construction and assignment happen at different boundaries
Record copy = original creates a new record and initializes its members from the source. It is copy initialization of a new object, not assignment to an already existing record despite the equals sign. By contrast, copy = another after copy already exists invokes assignment. Both rely on the member operations for this simple record, but they occur at different lifetime stages and can have different failure and performance characteristics.
Copy assignment of strings and vectors updates their owned values without requiring you to manually free old buffers. This does not make every compiler-generated assignment of every multi-member domain class transactional. If one member operation succeeds and a later one throws, a larger relationship between fields may need additional design. Resource ownership being managed correctly is a narrower promise than preserving every cross-member business invariant under every exception.
A const source can be copied because reading its value is sufficient. A const destination cannot ordinarily be assigned a new value after construction. That difference is useful when keeping a baseline record unchanged while experimenting with revisions. Avoid removing const from the baseline merely to make a mistaken assignment compile; identify whether the intended operation is creating a new version, replacing an existing one or deliberately mutating an alias.
Trace a copied integer field and a borrowed alias
The copy changes independently, whereas the reference names the original object. This example isolates aliasing without introducing pointer ownership.
Predict the output, edit the source, then select Run code.
Compare with the starter's expected output
Original: 5
Copy: 8
After editing inputs, calculate a new prediction; this reference describes only the starter.
Let resource-managing members do their jobs
The Rule of Zero recommends that ordinary types whose members already manage their resources generally do not define custom destructors, copy constructors, copy assignment operators, move constructors or move assignment operators just to manage those same resources again. Our record has string and vector members, so no manual delete or hand-written copying loop is needed. Leaving those special functions alone makes the intended ownership easier to review and reduces opportunities for double cleanup.
This is not a ban on ordinary constructors, validation functions or domain methods. A class may still need a constructor to establish a nonempty identifier or a checked mutation to preserve a numeric bound. The guideline concerns responsibility for special lifetime and transfer operations, not whether all types should become unrestricted public bags of data. Prefer established owning library members, then evaluate whether their generated combinations match the domain's actual semantics.
A user-defined destructor added only to print goodbye can also affect which move operations are implicitly generated. Avoid casually changing the set of special functions just to instrument an otherwise ordinary value type. The lifetime lesson uses a separate deliberately noncopyable tracing type to demonstrate cleanup; it is not the template for every record. Later ownership lessons explain when a resource handle genuinely needs a custom resource-management type or an existing smart pointer.
An independent container does not imply independent pointees or valid views
The integers in vector<int> are values. Replacing the element type with pointers or shared owners would change what copying means for the objects they refer to. A copied container can be independent while its elements still refer to the same external resource. Do not generalize this lesson's result into a promise that every possible nested structure is deeply duplicated. Choose the representation by the ownership and identity relationships the application needs.
Similarly, copying a reference-like view does not copy the underlying text or extend its owner's lifetime. This lesson avoids string_view and pointer members so the independence argument stays simple. If you retain a reference to an element and then grow a vector, the earlier iterator lesson's invalidation rules still apply; record copying does not repair a dangling alias. Track who owns each value and when a borrowed access path can stop being valid.
Copying a large vector has work proportional to the elements it copies; the convenience of value semantics is not a claim that all copies are free. Pass a const reference when a function only needs to observe an existing record during the call. Return owned values when the interface requires an independent result. Later move semantics can reduce some transfer costs, but it should not be used to hide an unclear ownership contract or justify premature low-level memory management.
Test what remains unchanged as carefully as what changes
The lab reads an original title, a count from zero through five, that many integer reading lines in minus one hundred through one hundred, a replacement title and one appended reading in the same numeric range. Each title must contain one through forty bytes and at least one byte other than an ordinary space. This is a documented byte-level text policy, not a Unicode normalization rule. All required input must be accepted before any report is printed.
Construct the original record, copy it into a new object, replace only the copy's title and append only to its vector. Print both titles and both complete sequences with one space before each displayed integer. The original sequence may be empty, and the copy then contains only the appended value. At most six readings exist in the revised sequence, so the lab stays bounded without falsely claiming that copying arbitrary records always uses constant memory.
Test zero original readings, the five-reading maximum, negative and repeated values, both title-length endpoints, malformed counts, missing required input and an invalid appended value. Compare every original value after the edits, not just the size: overwriting an original integer could preserve its count while violating independence. As a repair exercise, deliberately declare the revision as a reference in a copy of your work, observe the unexpected original changes, then restore a real value copy.
Build and investigate locally
Complete single-file programs use the compiler-backed editor when its sandbox is available. Otherwise save one program as main.cpp and build with your installed C++20 compiler. Expected output is a comparison target, not a substitute for execution.
macOS / Linux (choose your compiler):
clang++ -std=c++20 -Wall -Wextra -Wpedantic main.cpp -o lesson
# Or: g++ -std=c++20 -Wall -Wextra -Wpedantic main.cpp -o lesson
./lesson
Windows Developer Command Prompt:
cl /std:c++20 /EHsc /W4 main.cpp /Fe:lesson.exe
lesson.exeIndependent lab
Copy a record owning a title and readings, revise only the copy, and print evidence that every original value remains unchanged.
Acceptance checklist
- Use a record with std::string and std::vector<int>, with no user-defined special lifetime functions.
- Read original title, count 0..5, required readings -100..100, replacement title and appended reading -100..100, each on its own line.
- Accept titles of 1–40 bytes containing something other than ordinary spaces; reject bad or missing required input as Invalid input with status 1.
- Create a value copy, then change only its title and append one value.
- Print Original title, Original values, Copy title and Copy values; empty sequences must not require front or back.
Need a hint?
- Use auto revised = original, not auto& revised = original.
- A title-validation helper can be shared by both input title checks.
- Print every sequence element with a leading space so empty output lines need no special indexing.
Write and run your lab
Build your own solution before revealing the reference. Use the input field for normal, boundary and invalid cases; this runner executes your program but does not grade it.
Predict the output, edit the source, then select Run code.
Your lab evidence and notes
Record predictions, actual output, commands and the cause of each repair. Code is plain text: this scratchpad does not execute or grade C++.
Loading local notes…
Review your acceptance checklist and quiz explanations before marking this lesson complete. This is self-review, not a grade or certification.
Reveal the explained lab solution
Reference lab solution
Compare the reference with your own reasoning. Running this solution does not grade or complete the lesson.
Predict the output, edit the source, then select Run code.
Expected output
Original title: Morning
Original values: 2 4
Copy title: Revised
Copy values: 2 4 8
Accepted input produces a record with ordinary owning members; no custom destructor or copy routine is needed to manage their storage.
The revision is a new value, so its subsequent string assignment and vector append do not mutate the original members.
Full sequence output checks content independence even for empty data, while validation bounds the original to five readings and the revision to six.
Review before moving on
Explain the lesson's objectives in your own words, compare your lab evidence with the acceptance checklist, and keep the first failing case alongside your repair. Completing a page is not proof that every C++ feature or input is supported.
- Explain independent copies for a record owning string and vector members.
- Distinguish copy construction from copy assignment.
- Recognize aliasing through a reference as different from copying a value.
- Apply the Rule of Zero without confusing resource safety with domain validity.
- Test empty and nonempty copied collections without invalid access.
References and further reading
Original examples and exercises accompany these primary references. Tool-specific diagnostics and support depend on your installed version.