86 / 163 · C++11 · 12 min
Value copy versus shared ownership: the same copy, different promises
Whether copying an object copies the data or copies an entry point to the same data must be made clear by the type's interface. Contrast vector's independent values with shared_ptr's shared objects, and use weak_ptr to verify that observers do not extend the resource's lifetime.
In this lesson
C++ Primer
The extractable body text, code, exercises, terminology, and Appendix A plus the index of chapters 1–19 have been read in consecutive ranges; this is not limited to sample chapters. The image tables of Appendix A.1 have been additionally read from PDF pages 1057–1063. The research copy still contains tables, figures, and code that exist only as images and have not been restored one by one; some code transcriptions are truncated. Therefore no claim is made that every figure and piece of text in the original edition has been read in full. Exercises were not executed.
Edition, actual reading range, and original sources →First decide what copying means in the business logic
In the book, dynamic memory and copy control are tightly connected: the language can copy member by member, but it cannot decide for you whether a member represents independent data or shared responsibility. Copying a vector yields an independent sequence; modifying the copy does not change the original. Copying a shared_ptr adds another owner of the same object and does not automatically copy the pointed-to object.
Both are genuine copies; they simply copy at different levels. When designing an interface, first answer whether “after the caller obtains a copy, can they still affect me,” then choose the representation. Do not assume that because a smart pointer is responsible for release it also provides value semantics; nor should exclusive or directly stored data be unconditionally changed to sharing merely to avoid a handwritten destructor.
Observers must acknowledge that the object may disappear
A weak_ptr stores a detectable observing relationship and does not count toward the number of strong owners. Before each actual use, call lock to obtain a local shared_ptr; as long as that local owner remains alive, the object will not disappear during use because other owners have left. When lock fails, the interface must define how the missing case is handled.
The example first copies the strong owner inside an inner scope to confirm both access the same vector; after exiting, the object is still owned by the outer variable. When the last strong owner leaves, the observer becomes expired. This is closer to the business contract than remembering a specific use_count number, and it also avoids treating a debug count as a thread-synchronization scheme.
Choose the smallest ownership mechanism
Prefer storing values directly for value objects; consider unique_ptr only when there is truly a single transfer of responsibility; use shared_ptr only when multiple independent parties really need to jointly keep the lifetime. weak_ptr solves observation and some cyclic-ownership problems; it does not automatically discover or break all strong-reference cycles.
This example uses facilities already present in C++11; they remain applicable in C++20. Concurrency is not demonstrated: the control block supports certain concurrent operations among different smart-pointer instances, which does not mean the pointed-to vector is automatically thread-safe. The book's handwritten containers and allocators are for understanding the mechanisms; explanations of older allocator members and certain copy-elision rules must be re-evaluated against the current standard and cannot be taken as-is as a modern library implementation.
Pitfalls
- Copying a shared_ptr does not deep-copy the pointed-to object; two interfaces may be modifying the same data.
- The local strong reference obtained from weak_ptr::lock temporarily extends the lifetime; an expired check cannot replace a subsequent lock at the point of use.
Run an example
Minimum C++11 · complete program · Download .cpp
#include <cassert>
#include <memory>
#include <vector>
int main() {
std::vector<int> original{1, 2};
auto independent = original;
independent[0] = 9;
assert(original[0] == 1 && independent[0] == 9);
std::weak_ptr<std::vector<int>> observer;
{
auto owner = std::make_shared<std::vector<int>>(original);
observer = owner;
{
auto shared = owner;
(*shared)[0] = 7;
assert((*owner)[0] == 7);
}
auto pinned = observer.lock();
assert(pinned && (*pinned)[0] == 7);
}
assert(observer.expired());
assert(!observer.lock());
}
Compile locally
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread books-cpp-primer.cpp -o example && ./exampleExpected result
Expected: exit 0, no output; every assert holds.
CHECK YOUR UNDERSTANDING
Close the answer. Explain it.
If you want modifications through shared not to affect owner, how should you create the copy?
Show a reference answer
Change auto shared = owner to auto shared = std::make_shared<std::vector<int>>(*owner). This first copies the vector value, then creates an independent ownership relationship. After modifying (*shared)[0], (*owner)[0] should remain 1. Do not construct another shared_ptr from owner.get(); that would produce a separate control block and double-delete the same object.
Check the sources
Drafts and official chapters change. The version mark is only the example’s minimum.