Step 7 of 8
The rule of zero
The last few steps showed how much careful code a class needs when it holds a raw owning pointer: a destructor, a deep-copying copy constructor, copy assignment, and (later) move operations. Getting all of them right is hard, and it's easy to forget one.
The modern answer: don't hold raw owning pointers. Use members that already manage themselves:
std::vector<T>instead ofnew T[n]std::stringinstead ofnew char[n]std::unique_ptr<T>instead ofnew T
Their own destructors, copies and moves are correct, and the compiler-generated versions for your class simply call theirs, member by member. So you write none of the special functions. This is the rule of zero, and it's how most classes in modern C++ are written.
#include <iostream>
#include <string>
#include <vector>
class Playlist {
public:
void add(const std::string& song) { songs_.push_back(song); }
int count() const { return songs_.size(); }
const std::string& first() const { return songs_.front(); }
private:
std::string name_ = "mix";
std::vector<std::string> songs_;
};
int main() {
Playlist a;
a.add("intro");
a.add("outro");
Playlist b = a;
b.add("bonus");
std::cout << a.count() << " " << b.count() << " " << b.first() << "\n";
}
2 3 intro
Copying a into b makes a real, independent copy of the vector of strings, with no code from you. Destroying both frees everything correctly.
When do you still write them?
Only in low-level classes whose whole job is to wrap one resource (like the IntArray you just wrote, or a wrapper for a C library handle). Everything else composes those building blocks and follows the rule of zero.
Your turn: write class Inventory with the rule of zero: void add(const std::string& item), int count() const, bool has(const std::string& item) const, void clear(). No new, no delete, no destructor.
Previous: Copy assignment with copy-and-swap Next: Challenge: a scope guard