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 task

Write class Squad that has a std::vector<std::string> of names, with a maximum size of 5:

Challenge 2

Write class Backpack that has a std::map<std::string, int> of item counts and a weight limit. bool add(const std::string& item, int qty) fails (changing nothing) if it would take the total over the limit or qty <= 0. bool remove(const std::string& item, int qty) fails if there aren't that many, and erases an item whose count reaches 0. int total() const and int count(const std::string& item) const.

Challenge 3

Composition also lets you swap behaviour at run time. A Bot has a std::unique_ptr<Strategy>. Write Aggressive (always "push") and Careful ("retreat" below 50 hp, otherwise "hold"), then Bot::set_strategy and Bot::act(int hp), which returns "idle" when the bot has no strategy.

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