C/C++ Arena

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

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:

Previous: Calling the base version, and protected Next: Casts, dynamic_cast and slicing