Modernes C++ programmieren

Okt 20, 2024

listings-Chap18-README-onepage.md

Listings of Chap18.docx

This is the list of listings on one page. You can also view a linked summary.

Listing 18.1: With “friend”, you can allow another class access to private members.

Book listing lst-0001-book.cpp:

// https://godbolt.org/z/sTosKfocx 
#include <iostream>
class Mechanic;
class Thing {
    int value_; // private
public:
    explicit Thing(int value) : value_{value} {}
    void increment() { ++value_; }
    std::ostream& print(std::ostream& os) const { return os << value_; }
    friend class Mechanic;
};
class Mechanic {
    const Thing &thing_;
public:
    explicit Mechanic(const Thing &thing) : thing_{thing} {}
    auto getThingValue() const {
        return thing_.value_;        // access to private member of Thing
    }
};
int main() {
    Thing thing{45};
    thing.print(std::cout) << '\n';                // output: 45
    Mechanic mechanic{thing};
    thing.increment();                             // change internal value
    std::cout << mechanic.getThingValue() << '\n'; // output: 46
}

Godbolt Listing lst-0001-godb.cpp, https://godbolt.org/z/sTosKfocx:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/sTosKfocx 
#include <iostream>
class Mechanic;
class Thing {
    int value_; // private
public:
    explicit Thing(int value) : value_{value} {}
    void increment() { ++value_; }
    std::ostream& print(std::ostream& os) const { return os << value_; }
    friend class Mechanic;
};
class Mechanic {
    const Thing &thing_;
public:
    explicit Mechanic(const Thing &thing) : thing_{thing} {}
    auto getThingValue() const {
        return thing_.value_;        // access to private member of Thing
    }
};
int main() {
    Thing thing{45};
    thing.print(std::cout) << '\n';                // output: 45
    Mechanic mechanic{thing};
    thing.increment();                             // change internal value
    std::cout << mechanic.getThingValue() << '\n'; // output: 46
}

Listing 18.2: The Tree class has access to private members of Node.

Book listing lst-0002-book.cpp:

// https://godbolt.org/z/oxbcE18TM
#include <memory> // unique_ptr
#include <iostream>
#include <string>
using std::unique_ptr; using std::cout;
template <typename K, typename D> class Tree; // forward declaration
template <typename K, typename D>
class Node {
    friend class Tree<K,D>; // allow access to private members
    K key;
    D data;
    unique_ptr<Node> left, right;
public:
    Node(const K& k, const D& d) : key(k), data(d) { }
};
template <typename K, typename D>
class Tree {
public:
    void insert(const K &key, const D& data);
    D* find(const K &key) { return findRec(key, root); }
private:
    D* findRec(const K &key, unique_ptr<Node<K,D>> &node);
    unique_ptr<Node<K,D>> root;
};
template <typename K, typename D>
void Tree<K,D>::insert(const K& key, const D& data) {
    auto *current = &root;
    while(*current) { // as long as unique_ptr contains something
        auto &node = *(current->get());
        if (key < node.key) {
            current = &node.left;
        } else if (node.key < key) {
            current = &node.right;
        }
    }
    *current = std::make_unique<Node<K,D>>(key,data);
};
template <typename K, typename D>
D* Tree<K,D>::findRec(const K& key, unique_ptr<Node<K,D>> &where) {
    if(!where)
        return nullptr;
    auto &node = *(where.get());
    if(key < node.key)
        return findRec(key, node.left);
    if(node.key < key)
        return findRec(key, node.right);
    return &node.data; // key == node.key
};
int main() {
    Tree<int,std::string> bt {};
    bt.insert(3, "three");
    bt.insert(2, "two");
    bt.insert(4, "four");
    auto where = bt.find(7);
    if(where==nullptr) cout<<"no 7\n";      // output: no 7
    where = bt.find(3);
    if(where!=nullptr) cout<<*where<<"\n";  // output: three
}

Godbolt Listing lst-0002-godb.cpp, https://godbolt.org/z/oxbcE18TM:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/oxbcE18TM
#include <memory> // unique_ptr
#include <iostream>
#include <string>
using std::unique_ptr; using std::cout;
template <typename K, typename D> class Tree; // forward declaration
template <typename K, typename D>
class Node {
    friend class Tree<K,D>; // allow access to private members
    K key;
    D data;
    unique_ptr<Node> left, right;
public:
    Node(const K& k, const D& d) : key(k), data(d) { }
};
template <typename K, typename D>
class Tree {
public:
    void insert(const K &key, const D& data);
    D* find(const K &key) { return findRec(key, root); }
private:
    D* findRec(const K &key, unique_ptr<Node<K,D>> &node);
    unique_ptr<Node<K,D>> root;
};
template <typename K, typename D>
void Tree<K,D>::insert(const K& key, const D& data) {
    auto *current = &root;
    while(*current) { // as long as unique_ptr contains something
        auto &node = *(current->get());
        if (key < node.key) {
            current = &node.left;
        } else if (node.key < key) {
            current = &node.right;
        }
    }
    *current = std::make_unique<Node<K,D>>(key,data);
};
template <typename K, typename D>
D* Tree<K,D>::findRec(const K& key, unique_ptr<Node<K,D>> &where) {
    if(!where)
        return nullptr;
    auto &node = *(where.get());
    if(key < node.key)
        return findRec(key, node.left);
    if(node.key < key)
        return findRec(key, node.right);
    return &node.data; // key == node.key
};
int main() {
    Tree<int,std::string> bt {};
    bt.insert(3, "three");
    bt.insert(2, "two");
    bt.insert(4, "four");
    auto where = bt.find(7);
    if(where==nullptr) cout<<"no 7\n";      // output: no 7
    where = bt.find(3);
    if(where!=nullptr) cout<<*where<<"\n";  // output: three
}

Listing 18.3: When inheriting, you can specify what should become visible.

Book listing lst-0004-book.cpp:

// https://godbolt.org/z/eKbK39nG1 
class Base {
public:
    int xPublic = 1;
protected:
    int xProtected = 2;
private:
    int xPrivate = 3;
};
class DerivedPublic : public Base {
    // xPublic becomes 'public'
    // xProtected becomes 'protected'
    // xPrivate is not visible here
};
class DerivedProtected : protected Base {
    // xPublic becomes 'protected'
    // xProtected becomes 'protected'
    // xPrivate is not visible here
};
class DerivedPrivate : private Base { // or if nothing is specified
    // xPublic becomes 'private'
    // xProtected becomes 'private'
    // xPrivate is not visible here
};

Godbolt Listing lst-0004-godb.cpp, https://godbolt.org/z/eKbK39nG1:

//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/eKbK39nG1 
class Base {
public:
    int xPublic = 1;
protected:
    int xProtected = 2;
private:
    int xPrivate = 3;
};
class DerivedPublic : public Base {
    // xPublic becomes 'public'
    // xProtected becomes 'protected'
    // xPrivate is not visible here
};
class DerivedProtected : protected Base {
    // xPublic becomes 'protected'
    // xProtected becomes 'protected'
    // xPrivate is not visible here
};
class DerivedPrivate : private Base { // or if nothing is specified
    // xPublic becomes 'private'
    // xProtected becomes 'private'
    // xPrivate is not visible here
};

Listing 18.4: Inherited visibilities, viewed from the outside.

Book listing lst-0005-book.cpp:

// https://godbolt.org/z/61EdcjPn4
#include <iostream>
using std::cout; using std::ostream;
// ... as before ...
int main() {
    // public inheritance
    DerivedPublic dpu{};
    cout << dpu.xPublic << '\n';    // output: 1
    cout << dpu.xProtected << '\n'; // no access from outside
    // protected inheritance
    DerivedProtected dpt{};
    cout << dpt.xPublic << '\n';    // no access from outside
    cout << dpt.xProtected << '\n'; // no access from outside
    // private inheritance
    DerivedPrivate dpv{};
    cout << dpv.xPublic << '\n';    // no access from outside
    cout << dpv.xProtected << '\n'; // no access from outside
}

Godbolt Listing lst-0005-godb.cpp, https://godbolt.org/z/61EdcjPn4:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/61EdcjPn4
#include <iostream>
using std::cout; using std::ostream;
// ... as before ...
int main() {
    // public inheritance
    DerivedPublic dpu{};
    cout << dpu.xPublic << '\n';    // output: 1
    cout << dpu.xProtected << '\n'; // no access from outside
    // protected inheritance
    DerivedProtected dpt{};
    cout << dpt.xPublic << '\n';    // no access from outside
    cout << dpt.xProtected << '\n'; // no access from outside
    // private inheritance
    DerivedPrivate dpv{};
    cout << dpv.xPublic << '\n';    // no access from outside
    cout << dpv.xProtected << '\n'; // no access from outside
}

Listing 18.5: Inherited visibilities for further derivations.

Book listing lst-0006-book.cpp:

// https://godbolt.org/z/Pq5aWq8xr
#include <iostream>
using std::cout; using std::ostream;
// ... as before ...
struct NormalCase : public DerivedPublic {
    void print() {
        cout << xPublic;
        cout << xProtected;
    }
};
struct SpecialCase : public DerivedPrivate {
    void print() {
        cout << xPublic;                   // (ERR) no access
        cout << xProtected;                // (ERR) no access
    }
};
int main() {
    NormalCase n {};
    n.print();                // output: 12
    SpecialCase s {};
    s.print();
}

Godbolt Listing lst-0006-godb.cpp, https://godbolt.org/z/Pq5aWq8xr:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/Pq5aWq8xr
#include <iostream>
using std::cout; using std::ostream;
// ... as before ...
struct NormalCase : public DerivedPublic {
    void print() {
        cout << xPublic;
        cout << xProtected;
    }
};
struct SpecialCase : public DerivedPrivate {
    void print() {
        cout << xPublic;                   // (ERR) no access
        cout << xProtected;                // (ERR) no access
    }
};
int main() {
    NormalCase n {};
    n.print();                // output: 12
    SpecialCase s {};
    s.print();
}

Listing 18.6: In practice, child classes use privately inherited members indirectly.

Book listing lst-0007-book.cpp:

// https://godbolt.org/z/WYqYe7PoY
#include <iostream>
using std::cout; using std::ostream;
class Base {
    public: int data = 5;
};
class Middle : private Base {
protected: void print() {
        cout << data;   // 'data' is inherited privately here
    }
};
class Ultimately : public Middle {
    public: void go() {
        // 'data' is not visible
        print();        // 'print' is protected visible
    }
};
int main() {
    Ultimately u {};
    u.go();             // output: 5
}

Godbolt Listing lst-0007-godb.cpp, https://godbolt.org/z/WYqYe7PoY:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/WYqYe7PoY
#include <iostream>
using std::cout; using std::ostream;
class Base {
    public: int data = 5;
};
class Middle : private Base {
protected: void print() {
        cout << data;   // 'data' is inherited privately here
    }
};
class Ultimately : public Middle {
    public: void go() {
        // 'data' is not visible
        print();        // 'print' is protected visible
    }
};
int main() {
    Ultimately u {};
    u.go();             // output: 5
}

Listing 18.7: A has-a relationship via private inheritance.

Book listing lst-0008-book.cpp:

// https://godbolt.org/z/ETr73T5a9
class Engine {
public:
  Engine(int numCylinders);
  void start();                   // start engine
};
class Car : private Engine {      // Car has a engine
public:
  Car() : Engine{8} { }           // initializes a car with 8 cylinders
  using Engine::start;            // starts car by starting the engine
};

Godbolt Listing lst-0008-godb.cpp, https://godbolt.org/z/ETr73T5a9:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/ETr73T5a9
class Engine {
public:
  Engine(int numCylinders);
  void start();                   // start engine
};
class Car : private Engine {      // Car has a engine
public:
  Car() : Engine{8} { }           // initializes a car with 8 cylinders
  using Engine::start;            // starts car by starting the engine
};

Listing 18.8: Has-a relationship using a member.

Book listing lst-0009-book.cpp:

// https://godbolt.org/z/67EjhMf54
class Car {
public:
  Car() : engine_{8} { }            // initializes a car with 8 cylinders
  void start() { engine_.start(); } // starts the car by starting the engine
private:
  Engine engine_;                   // Car has-a engine
};

Godbolt Listing lst-0009-godb.cpp, https://godbolt.org/z/67EjhMf54:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/67EjhMf54
class Car {
public:
  Car() : engine_{8} { }            // initializes a car with 8 cylinders
  void start() { engine_.start(); } // starts the car by starting the engine
private:
  Engine engine_;                   // Car has-a engine
};

Listing 18.9: A signature class has only pure virtual methods.

Book listing lst-0010-book.cpp:

// https://godbolt.org/z/rW4oTPhrG 
struct Driver {
    virtual void init() = 0;
    virtual void done() = 0;
    virtual bool send(const char* data, unsigned len) = 0;
};

Godbolt Listing lst-0010-godb.cpp, https://godbolt.org/z/rW4oTPhrG:

//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/rW4oTPhrG 
struct Driver {
    virtual void init() = 0;
    virtual void done() = 0;
    virtual bool send(const char* data, unsigned len) = 0;
};

Listing 18.10: A signature class makes a good base class.

Book listing lst-0011-book.cpp:

// https://godbolt.org/z/cevM7nq53
#include <iostream>
using std::cout;
struct KeyboardDriver : public Driver {
    void init() override { cout << "Init Keyboard\n"; }
    void done() override { cout << "Done Keyboard\n"; }
    bool send(const char* data, unsigned int len) override {
        cout << "sending " << len << " bytes\n";
        return true;
    }
};
struct Computer {
    Driver &driver_;
    explicit Computer(Driver &driver) : driver_{driver} {
        driver_.init();
    }
    void run() {
        driver_.send("Hello", 5);
    }
    ~Computer() {
        driver_.done();
    }
    Computer(const Computer&) = delete;
};
int main() {
    KeyboardDriver keyboard {};
    Computer computer(keyboard); // Output: Init Keyboard
    computer.run();              // Output: sending 5 bytes
}                                // Output: Done Keyboard

Godbolt Listing lst-0011-godb.cpp, https://godbolt.org/z/cevM7nq53:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/cevM7nq53
#include <iostream>
using std::cout;
struct KeyboardDriver : public Driver {
    void init() override { cout << "Init Keyboard\n"; }
    void done() override { cout << "Done Keyboard\n"; }
    bool send(const char* data, unsigned int len) override {
        cout << "sending " << len << " bytes\n";
        return true;
    }
};
struct Computer {
    Driver &driver_;
    explicit Computer(Driver &driver) : driver_{driver} {
        driver_.init();
    }
    void run() {
        driver_.send("Hello", 5);
    }
    ~Computer() {
        driver_.done();
    }
    Computer(const Computer&) = delete;
};
int main() {
    KeyboardDriver keyboard {};
    Computer computer(keyboard); // Output: Init Keyboard
    computer.run();              // Output: sending 5 bytes
}                                // Output: Done Keyboard

Listing 18.11: A virtual method with implementation “= 0” is abstract.

Book listing lst-0012-book.cpp:

// https://godbolt.org/z/xWr7axonx
#include <iostream>           // cout
#include <memory>             // unique_ptr
using std::cout;
class Driver {                // abstract base class
public:
    virtual void init() = 0;
    virtual void done() = 0;
    virtual void send(const std::string &data) = 0;
};
class ProductionDriver : public Driver {
public:
    void init() override { }
    void done() override { }
    void send(const std::string &data) override { cout << data << "\n"; }
};
class DebuggingDriver : public Driver {
   size_t countSend_ = 0;
public:
    void init() override {
        countSend_= 0; cout << "Ok, I'm initialized.\n";
    }
    void done() override {
        cout << "send used:" << countSend_ << " times\n";
    }
    void send(const std::string &data) override {
        cout << "send("<<countSend_<<"):"<< data << "\n";
        ++countSend_;
    }
};
struct DriverWrapper {        // RAII wrapper for init() and done()
    Driver &driver_;
    explicit DriverWrapper(Driver& driver) : driver_(driver) {
        driver_.init(); }
    ~DriverWrapper() { driver_.done(); }
    DriverWrapper(const DriverWrapper&) = delete; // no copying
};

void doWork(Driver &driver) { // someone who flexibly uses any driver
    DriverWrapper wrapper(driver);      // call init() and done()
    driver.send("An Unexpected Journey");
    driver.send("The Desolation Of Smaug");
}

int main() {
    // same doWork, once with production and once with debugging driver
    ProductionDriver production{};
    doWork( production );
    DebuggingDriver debugging{};
    doWork( debugging );

    // more common variant of a dynamically created driver
    std::unique_ptr<Driver> driver{ new ProductionDriver{} };
    doWork( *driver );
}

Godbolt Listing lst-0012-godb.cpp, https://godbolt.org/z/xWr7axonx:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/xWr7axonx
#include <iostream>           // cout
#include <memory>             // unique_ptr
using std::cout;
class Driver {                // abstract base class
public:
    virtual void init() = 0;
    virtual void done() = 0;
    virtual void send(const std::string &data) = 0;
};
class ProductionDriver : public Driver {
public:
    void init() override { }
    void done() override { }
    void send(const std::string &data) override { cout << data << "\n"; }
};
class DebuggingDriver : public Driver {
   size_t countSend_ = 0;
public:
    void init() override {
        countSend_= 0; cout << "Ok, I'm initialized.\n";
    }
    void done() override {
        cout << "send used:" << countSend_ << " times\n";
    }
    void send(const std::string &data) override {
        cout << "send("<<countSend_<<"):"<< data << "\n";
        ++countSend_;
    }
};
struct DriverWrapper {        // RAII wrapper for init() and done()
    Driver &driver_;
    explicit DriverWrapper(Driver& driver) : driver_(driver) {
        driver_.init(); }
    ~DriverWrapper() { driver_.done(); }
    DriverWrapper(const DriverWrapper&) = delete; // no copying
};

void doWork(Driver &driver) { // someone who flexibly uses any driver
    DriverWrapper wrapper(driver);      // call init() and done()
    driver.send("An Unexpected Journey");
    driver.send("The Desolation Of Smaug");
}

int main() {
    // same doWork, once with production and once with debugging driver
    ProductionDriver production{};
    doWork( production );
    DebuggingDriver debugging{};
    doWork( debugging );

    // more common variant of a dynamically created driver
    std::unique_ptr<Driver> driver{ new ProductionDriver{} };
    doWork( *driver );
}

Listing 18.12: Multiple inheritance means having multiple base classes.

Book listing lst-0014-book.cpp:

// https://godbolt.org/z/xnGTM351G
#include <iostream>
using std::cout;
class Mammal {
public:
    void giveBirth() { cout << "Birth!\n"; }
};
class Flying {
public:
    void fly() { cout << "Flight!\n"; }
};
class Bat: public Mammal, public Flying {
public:
    void call() { cout << "Ultrasound!\n"; }
};
int main() {
    Bat bruce{};
    bruce.giveBirth();    // Output: Birth!
    bruce.fly();          // Output: Flight!
    bruce.call();         // Output: Ultrasound!
}

Godbolt Listing lst-0014-godb.cpp, https://godbolt.org/z/xnGTM351G:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/xnGTM351G
#include <iostream>
using std::cout;
class Mammal {
public:
    void giveBirth() { cout << "Birth!\n"; }
};
class Flying {
public:
    void fly() { cout << "Flight!\n"; }
};
class Bat: public Mammal, public Flying {
public:
    void call() { cout << "Ultrasound!\n"; }
};
int main() {
    Bat bruce{};
    bruce.giveBirth();    // Output: Birth!
    bruce.fly();          // Output: Flight!
    bruce.call();         // Output: Ultrasound!
}

Listing 18.13: The clock and calendar combination results in a clock with a calendar.

Book listing lst-0015-book.cpp:

// https://godbolt.org/z/oxoef1nTs
// Parameters must be valid values of the respective unit.
#include <iostream> // ostream
#include <iomanip>  // setw, setfill
#include <format>
using std::ostream; using std::format;
class Clock {
protected:
    int h_, m_, s_;
public:
    Clock(int hours, int minutes, int seconds)
    : h_{hours}, m_{minutes}, s_{seconds} {}
    void setClock(int hours, int minutes, int seconds) {
        h_ = hours; m_ = minutes; s_ = seconds;
    }
    friend ostream& operator<<(ostream&os, const Clock& c) {
        return os << format("{:02}:{:02}:{:02}", c.h_, c.m_, c.s_);
    }
    void tick() { // +1 second
        if(s_ >= 59) {
            s_ = 0;
            if(m_ >= 59) {
                m_ = 0;
                if(h_ >= 23) h_ = 0;
                else { h_ += 1; }
            } else { m_ += 1; }
        } else { s_ += 1; }
    }
};

Godbolt Listing lst-0015-godb.cpp, https://godbolt.org/z/oxoef1nTs:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/oxoef1nTs
// Parameters must be valid values of the respective unit.
#include <iostream> // ostream
#include <iomanip>  // setw, setfill
#include <format>
using std::ostream; using std::format;
class Clock {
protected:
    int h_, m_, s_;
public:
    Clock(int hours, int minutes, int seconds)
    : h_{hours}, m_{minutes}, s_{seconds} {}
    void setClock(int hours, int minutes, int seconds) {
        h_ = hours; m_ = minutes; s_ = seconds;
    }
    friend ostream& operator<<(ostream&os, const Clock& c) {
        return os << format("{:02}:{:02}:{:02}", c.h_, c.m_, c.s_);
    }
    void tick() { // +1 second
        if(s_ >= 59) {
            s_ = 0;
            if(m_ >= 59) {
                m_ = 0;
                if(h_ >= 23) h_ = 0;
                else { h_ += 1; }
            } else { m_ += 1; }
        } else { s_ += 1; }
    }
};

Listing 18.14: A simple calendar class.

Book listing lst-0017-book.cpp:

// https://godbolt.org/z/56Ec357Tf
// … includes and usings …
struct Calendar {
    int y_, m_, d_;
    static const std::vector<int> mlens_;
    bool leapyear() const {
        if(y_ % 4 != 0) return false;
        if(y_ % 100 != 0) return true;
        if(y_ % 400 != 0) return false;
        return true;
    }
public:
    Calendar(int year, int month, int day)
    : y_{year}, m_{month}, d_{day} {}
    void setCalendar(int year, int month, int day) {
        y_ = year; m_ = month; d_ = day;
    }
    friend ostream& operator<<(ostream& os, const Calendar& c) {
        return os << format("{:04}-{:02}-{:02}", c.y_, c.m_, c.d_);
    }
    void advance() { // +1 day
        auto maxd = mlens_[m_-1];  // 0-based vector
        if(m_==2 && leapyear())
            maxd += 1;             // February in a leap year
        if(d_ >= maxd) {
            d_ = 1;
            if(m_ >= 12) { m_ = 1; y_ += 1; }
            else { m_ += 1; }
        } else { d_ += 1; }
    }
};
const std::vector<int> Calendar::mlens_ = {31,28,31,30,31,30,31,31,30,31,30,31};

Godbolt Listing lst-0017-godb.cpp, https://godbolt.org/z/56Ec357Tf:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/56Ec357Tf
// … includes and usings …
struct Calendar {
    int y_, m_, d_;
    static const std::vector<int> mlens_;
    bool leapyear() const {
        if(y_ % 4 != 0) return false;
        if(y_ % 100 != 0) return true;
        if(y_ % 400 != 0) return false;
        return true;
    }
public:
    Calendar(int year, int month, int day)
    : y_{year}, m_{month}, d_{day} {}
    void setCalendar(int year, int month, int day) {
        y_ = year; m_ = month; d_ = day;
    }
    friend ostream& operator<<(ostream& os, const Calendar& c) {
        return os << format("{:04}-{:02}-{:02}", c.y_, c.m_, c.d_);
    }
    void advance() { // +1 day
        auto maxd = mlens_[m_-1];  // 0-based vector
        if(m_==2 && leapyear())
            maxd += 1;             // February in a leap year
        if(d_ >= maxd) {
            d_ = 1;
            if(m_ >= 12) { m_ = 1; y_ += 1; }
            else { m_ += 1; }
        } else { d_ += 1; }
    }
};
const std::vector<int> Calendar::mlens_ = {31,28,31,30,31,30,31,31,30,31,30,31};

Listing 18.15: The calendar clock is a calendar and a clock.

Book listing lst-0019-book.cpp:

// https://godbolt.org/z/zh5cPG7TK
class CalendarClock : public Clock, public Calendar {
public:
    CalendarClock(int y, int m, int d, int hh, int mm, int ss)
    : Calendar{y,m,d}, Clock{hh,mm,ss} {}
    void tick() {         // +1 second
        auto prev_h = h_;
        Clock::tick();    // Call base class method
        if(h_ < prev_h) { // if new day
            advance();    // … advance calendar
        }
    }
    friend ostream& operator<<(ostream&os, const CalendarClock& cc) {
        operator<<(os, (Calendar&)cc) << " "; // Call free function
        operator<<(os, (Clock&)cc);           // Call free function
        return os;
    }
};

Godbolt Listing lst-0019-godb.cpp, https://godbolt.org/z/zh5cPG7TK:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/zh5cPG7TK
class CalendarClock : public Clock, public Calendar {
public:
    CalendarClock(int y, int m, int d, int hh, int mm, int ss)
    : Calendar{y,m,d}, Clock{hh,mm,ss} {}
    void tick() {         // +1 second
        auto prev_h = h_;
        Clock::tick();    // Call base class method
        if(h_ < prev_h) { // if new day
            advance();    // … advance calendar
        }
    }
    friend ostream& operator<<(ostream&os, const CalendarClock& cc) {
        operator<<(os, (Calendar&)cc) << " "; // Call free function
        operator<<(os, (Clock&)cc);           // Call free function
        return os;
    }
};

Listing 18.16: With multiple inheritance, the value of a pointer can change.

Book listing lst-0023-book.cpp:

// https://godbolt.org/z/oc7jsbrEK 
#include <iostream>
using std::cout;
struct Base1 {
    virtual void f1() {}
};
struct Base2 {
    virtual void f2() {}
};
struct Derived : public Base1, public Base2 {
    virtual void g() {};
};
void compare(void* a, void* b) {
    cout << (a==b ? "identical\n" : "different\n");
}
int main() {
    Derived d{};
    auto *pd = &d;
    cout << pd << '\n';           // for example 0x1000
    auto pb1 = static_cast<Base1*>(pd);
    cout << pb1 << '\n';          // still 0x1000
    auto pb2 = static_cast<Base2*>(pd);
    cout << pb2 << '\n';          // now 0x1008!
    cout << (pd==pb1 ? "same\n" : "different\n"); // Output: same
    cout << (pd==pb2 ? "same\n" : "different\n"); // Output: same
    compare(pb1, pd);             // Output: identical
    compare(pb2, pd);             // Output: different
}

Godbolt Listing lst-0023-godb.cpp, https://godbolt.org/z/oc7jsbrEK:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/oc7jsbrEK 
#include <iostream>
using std::cout;
struct Base1 {
    virtual void f1() {}
};
struct Base2 {
    virtual void f2() {}
};
struct Derived : public Base1, public Base2 {
    virtual void g() {};
};
void compare(void* a, void* b) {
    cout << (a==b ? "identical\n" : "different\n");
}
int main() {
    Derived d{};
    auto *pd = &d;
    cout << pd << '\n';           // for example 0x1000
    auto pb1 = static_cast<Base1*>(pd);
    cout << pb1 << '\n';          // still 0x1000
    auto pb2 = static_cast<Base2*>(pd);
    cout << pb2 << '\n';          // now 0x1008!
    cout << (pd==pb1 ? "same\n" : "different\n"); // Output: same
    cout << (pd==pb2 ? "same\n" : "different\n"); // Output: same
    compare(pb1, pd);             // Output: identical
    compare(pb2, pd);             // Output: different
}

Listing 18.17: The observer pattern with multiple inheritance.

Book listing lst-0024-book.cpp:

// https://godbolt.org/z/EnE7Y886M 
#include <iostream>
#include <vector>
using std::cout;
// == Observer Design Pattern ==
struct Observer {
    virtual void update() = 0;
};
class Subject {
    std::vector<Observer*> observers_; // not owning pointers
protected:
    void notify() {
        for (auto o : observers_)
            o->update();
    }
public:
    void addObserver(Observer* o) {
        observers_.push_back(o);
    }
};
// == Concrete Class ==
struct MyThing {
    int calc() { return 1+1; }
};
// == bring together ==
struct MyObservableThing : public MyThing, public Subject {
    int calc() {
        notify();
        return MyThing::calc();
    }
};
// == observe something ==
struct MyObserver : public Observer {
    void update() override {
        cout << "observed\n";
    }
};
int main() {
    MyObserver myObserver{};
    MyObservableThing myObservableThing{};
    myObservableThing.addObserver(&myObserver);
    auto result = myObservableThing.calc(); // Output: observed
}

Godbolt Listing lst-0024-godb.cpp, https://godbolt.org/z/EnE7Y886M:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/EnE7Y886M 
#include <iostream>
#include <vector>
using std::cout;
// == Observer Design Pattern ==
struct Observer {
    virtual void update() = 0;
};
class Subject {
    std::vector<Observer*> observers_; // not owning pointers
protected:
    void notify() {
        for (auto o : observers_)
            o->update();
    }
public:
    void addObserver(Observer* o) {
        observers_.push_back(o);
    }
};
// == Concrete Class ==
struct MyThing {
    int calc() { return 1+1; }
};
// == bring together ==
struct MyObservableThing : public MyThing, public Subject {
    int calc() {
        notify();
        return MyThing::calc();
    }
};
// == observe something ==
struct MyObserver : public Observer {
    void update() override {
        cout << "observed\n";
    }
};
int main() {
    MyObserver myObserver{};
    MyObservableThing myObservableThing{};
    myObservableThing.addObserver(&myObserver);
    auto result = myObservableThing.calc(); // Output: observed
}

Listing 18.18: A simple example with virtual inheritance.

Book listing lst-0025-book.cpp:

// https://godbolt.org/z/8jYeYPs7W 
#include <iostream>
using std::cout;
class Base {
public:
  int data_ = 0;
};
class Derived1 : public virtual Base {
};
class Derived2 : public virtual Base {
};
class DerivedDerived : public Derived1, public Derived2 {
public:
  void method() {
     data_ = 1;  // unambiguous, because there is only one data_
  }
};
void output(const DerivedDerived &dd) {
    cout << dd.data_
        << (((Derived1&)dd).data_)
        << (((Derived2&)dd).data_)
        << (((Base&)dd).data_) << '\n';
}
int main() {
  DerivedDerived dd{};
  output(dd);    // Output: 0000
  dd.method();   // sets data_ to 1
  output(dd);    // Output: 1111
}

Godbolt Listing lst-0025-godb.cpp, https://godbolt.org/z/8jYeYPs7W:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/8jYeYPs7W 
#include <iostream>
using std::cout;
class Base {
public:
  int data_ = 0;
};
class Derived1 : public virtual Base {
};
class Derived2 : public virtual Base {
};
class DerivedDerived : public Derived1, public Derived2 {
public:
  void method() {
     data_ = 1;  // unambiguous, because there is only one data_
  }
};
void output(const DerivedDerived &dd) {
    cout << dd.data_
        << (((Derived1&)dd).data_)
        << (((Derived2&)dd).data_)
        << (((Base&)dd).data_) << '\n';
}
int main() {
  DerivedDerived dd{};
  output(dd);    // Output: 0000
  dd.method();   // sets data_ to 1
  output(dd);    // Output: 1111
}

Listing 18.19: Effectively, user() calls a sister method here.

Book listing lst-0026-book.cpp:

// https://godbolt.org/z/s9xnn7c5j 
#include <iostream>
using std::cout;
struct Base {                           // abstract class
  virtual void provider() = 0;
  virtual void user() = 0;
};
struct Derived1 : public virtual Base { // still abstract
  virtual void user() override { provider(); }
};
struct Derived2 : public virtual Base { // still abstract
  virtual void provider() override { cout << "Derived2::provider!\n"; }
};
struct DerivedDerived : public Derived1, public Derived2 { // concrete
};
int main() {
  DerivedDerived dd{};
  DerivedDerived *pdd = &dd;
  Derived1* pd1 = pdd; // Cast within the hierarchy
  Derived2* pd2 = pdd; // Cast within the hierarchy
  pdd->user();         // Output: Derived2::provider()!
  pd1->user();         // Output: Derived2::provider()!
  pd2->user();         // Output: Derived2::provider()!
}

Godbolt Listing lst-0026-godb.cpp, https://godbolt.org/z/s9xnn7c5j:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/s9xnn7c5j 
#include <iostream>
using std::cout;
struct Base {                           // abstract class
  virtual void provider() = 0;
  virtual void user() = 0;
};
struct Derived1 : public virtual Base { // still abstract
  virtual void user() override { provider(); }
};
struct Derived2 : public virtual Base { // still abstract
  virtual void provider() override { cout << "Derived2::provider!\n"; }
};
struct DerivedDerived : public Derived1, public Derived2 { // concrete
};
int main() {
  DerivedDerived dd{};
  DerivedDerived *pdd = &dd;
  Derived1* pd1 = pdd; // Cast within the hierarchy
  Derived2* pd2 = pdd; // Cast within the hierarchy
  pdd->user();         // Output: Derived2::provider()!
  pd1->user();         // Output: Derived2::provider()!
  pd2->user();         // Output: Derived2::provider()!
}

Listing 18.20: You create a literal data type with a constexpr constructor.

Book listing lst-0029-book.cpp:

// https://godbolt.org/z/Kvfx9Yeox 
#include <array>
class Value {
    int val_;
public:
    constexpr Value(int val) : val_{val} {};
    constexpr int get() const { return val_; }
};

constexpr Value five{5}; // works, Value is a literal data type

std::array<int,five.get()> data; // works, get is constexpr

Godbolt Listing lst-0029-godb.cpp, https://godbolt.org/z/Kvfx9Yeox:

//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/Kvfx9Yeox 
#include <array>
class Value {
    int val_;
public:
    constexpr Value(int val) : val_{val} {};
    constexpr int get() const { return val_; }
};

constexpr Value five{5}; // works, Value is a literal data type

std::array<int,five.get()> data; // works, get is constexpr

Listing 18.21: User-defined literals are especially useful with literal data types.

Book listing lst-0030-book.cpp:

// https://godbolt.org/z/r1vYc7r5j 
#include <array>
#include <iostream>
#include <type_traits> // is_literal_type
class Value {
    int val_;
public:
    constexpr Value(int val) : val_{val} {};
    constexpr operator int() const { return val_; }
};
namespace lit {
    constexpr Value operator"" _val(const char*, size_t sz) { 
      return Value(sz); }
}
struct Nope {
    constexpr Nope() {};
    virtual ~Nope() {};                 // non-constexpr destructor
};
int main() {
    using namespace lit;
    constexpr Value five{5};
    std::array<int,"11111"_val> data;  // use user-defined literal
    std::cout << data.size() << '\n';                        // Output: 5
    std::cout << std::boolalpha;
    std::cout << std::is_literal_type<Value>::value << '\n'; // Output: true
    std::cout << std::is_literal_type<Nope>::value << '\n';  // Output: false
}

Godbolt Listing lst-0030-godb.cpp, https://godbolt.org/z/r1vYc7r5j:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/r1vYc7r5j 
#include <array>
#include <iostream>
#include <type_traits> // is_literal_type
class Value {
    int val_;
public:
    constexpr Value(int val) : val_{val} {};
    constexpr operator int() const { return val_; }
};
namespace lit {
    constexpr Value operator"" _val(const char*, size_t sz) { 
      return Value(sz); }
}
struct Nope {
    constexpr Nope() {};
    virtual ~Nope() {};                 // non-constexpr destructor
};
int main() {
    using namespace lit;
    constexpr Value five{5};
    std::array<int,"11111"_val> data;  // use user-defined literal
    std::cout << data.size() << '\n';                        // Output: 5
    std::cout << std::boolalpha;
    std::cout << std::is_literal_type<Value>::value << '\n'; // Output: true
    std::cout << std::is_literal_type<Nope>::value << '\n';  // Output: false
}