Copying an object calls its copy constructor (new object) or copy assignment operator (existing object). If the class owns a raw resource, the default member-wise copy is shallow — and wrong.
Copy constructor vs copy assignment
#include <iostream>
struct Tag {
int v = 0;
Tag() = default;
Tag(const Tag& o) : v(o.v) { std::cout << "copy ctor\n"; }
Tag& operator=(const Tag& o) { v = o.v; std::cout << "copy assign\n"; return *this; }
};
void byValue(Tag) {}
int main() {
Tag a;
Tag b = a; // copy ctor: b is being created (the '=' here is not assignment)
Tag c(a); // copy ctor
c = b; // copy assignment: c already exists
byValue(a); // copy ctor for the parameter
}copy ctor
copy ctor
copy assign
copy ctorThe shallow-copy bug
#include <cstring>
class Buffer {
char* data_;
public:
explicit Buffer(const char* s) : data_(new char[std::strlen(s) + 1]) { std::strcpy(data_, s); }
~Buffer() { delete[] data_; }
// no copy constructor → the compiler copies the *pointer*
};
int main() {
Buffer a("hello");
Buffer b = a; // b.data_ == a.data_
} // both destructors delete[] the same block → double freeDeep copy done right
#include <algorithm>
#include <cstring>
#include <iostream>
#include <utility>
class Buffer {
std::size_t size_;
char* data_;
public:
explicit Buffer(const char* s) : size_(std::strlen(s)), data_(new char[size_ + 1]) {
std::copy(s, s + size_ + 1, data_);
}
~Buffer() { delete[] data_; }
Buffer(const Buffer& o) : size_(o.size_), data_(new char[o.size_ + 1]) { // deep copy
std::copy(o.data_, o.data_ + size_ + 1, data_);
}
Buffer& operator=(Buffer o) { // copy-and-swap: `o` is already a copy
swap(*this, o); // take its contents; old ones die with `o`
return *this;
}
friend void swap(Buffer& a, Buffer& b) noexcept {
std::swap(a.size_, b.size_);
std::swap(a.data_, b.data_);
}
void set(std::size_t i, char c) { data_[i] = c; }
const char* str() const { return data_; }
};
int main() {
Buffer a("hello");
Buffer b = a; // independent copy
b.set(0, 'j');
Buffer c("x");
c = a; // assignment
Buffer& alias = c;
c = alias; // self-assignment is safe
std::cout << a.str() << ' ' << b.str() << ' ' << c.str() << '\n';
}hello jello helloWhat's the difference between a shallow and a deep copy?
When is the copy constructor called?