Step 7 of 9
Composition over inheritance
Inheritance is powerful, but it's also the tightest coupling there is: a derived class depends on every detail of its base. For many relationships there's a simpler tool. Inheritance means is-a: a Truck is a Vehicle. For has-a relationships, give the class a member instead. That's called composition.
class Squad : public std::vector<Player> { }; // wrong: a squad isn't a vector
class Squad { std::vector<Player> members_; }; // right: a squad has members
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
class Playlist {
public:
bool add(const std::string& song) {
if (songs_.size() >= 3) return false; // rule: max 3
if (std::find(songs_.begin(), songs_.end(), song) != songs_.end()) return false;
songs_.push_back(song);
return true;
}
std::string summary() const {
std::string out;
for (const auto& s : songs_) out += "[" + s + "]";
return out;
}
private:
std::vector<std::string> songs_; // has-a: the playlist controls access
};
int main() {
Playlist p;
std::cout << p.add("a") << p.add("b") << p.add("a") << p.add("c") << p.add("d") << "\n";
std::cout << p.summary() << "\n";
}
11010
[a][b][c]
Why composition is the default in professional code
- The class controls its own interface. Inheriting from
std::vectorwould exposeerase,clear,push_backand everything else, letting callers break the "max 3, no duplicates" rules. - You can swap the member's type later (say, to a
std::set) without touching any caller. - Standard containers have no virtual destructor, so they aren't meant to be base classes at all.
Your task
Squad is the same shape as Playlist: add checks the size limit and duplicates, remove finds the name with std::find and erases it, and roster joins the names with ", " (watch out for the separator fencepost from the streams module).
Your turn: write class Squad that has a std::vector<std::string> of names, with a maximum size of 5:
bool add(const std::string& name): false if full or already in the squadbool remove(const std::string& name): false if not foundint size() const,std::string roster() const(names joined with", ", in join order)
Previous: Calling the base version, and protected Next: Casts, dynamic_cast and slicing