Modernes C++ programmieren

Okt 20, 2024

listings-Chap15-README-onepage.md

Listings of Chap15.docx

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

Listing 15.1: The common ancestor of our “Year”, “Month”, and “Day” helper classes.

Book listing lst-0005-book.cpp:

// https://godbolt.org/z/hhGo46z5a 
#include <iostream>  // ostream
#include <format>    // format, vformat, make_format_args
using std::ostream;
class Value {
protected: // not public, only for own and derived use
    int value_;
    const std::string fmt_;   // e.g. "{:02}" or "{:04}"
    Value(int v, unsigned w)  // constructor with two arguments
      : value_{v}, fmt_{std::format("{{:0{}}}", w)} {}
public:
    ostream& print(ostream& os) const;
};
ostream& operator<<(ostream& os, const Value& right) {
    return right.print(os);
}
ostream& Value::print(ostream& os) const {
    return os << std::vformat(fmt_, std::make_format_args(value_));
}

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

//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/hhGo46z5a 
#include <iostream>  // ostream
#include <format>    // format, vformat, make_format_args
using std::ostream;
class Value {
protected: // not public, only for own and derived use
    int value_;
    const std::string fmt_;   // e.g. "{:02}" or "{:04}"
    Value(int v, unsigned w)  // constructor with two arguments
      : value_{v}, fmt_{std::format("{{:0{}}}", w)} {}
public:
    ostream& print(ostream& os) const;
};
ostream& operator<<(ostream& os, const Value& right) {
    return right.print(os);
}
ostream& Value::print(ostream& os) const {
    return os << std::vformat(fmt_, std::make_format_args(value_));
}

Listing 15.2: The duplicate code of the helper classes has now disappeared.

Book listing lst-0006-book.cpp:

// https://godbolt.org/z/eYY1js4Te 
class Year : public Value {               // derive from class Value
public:
    explicit Year(int v) : Value{v, 4} {} // initialize base class
};
class Month : public Value {
public:
    explicit Month(int v) : Value{v, 2} {}
};
struct Day : public Value {               // class-public corresponds to struct
    explicit Day(int v) : Value{v, 2} {}
};

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

//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/eYY1js4Te 
class Year : public Value {               // derive from class Value
public:
    explicit Year(int v) : Value{v, 4} {} // initialize base class
};
class Month : public Value {
public:
    explicit Month(int v) : Value{v, 2} {}
};
struct Day : public Value {               // class-public corresponds to struct
    explicit Day(int v) : Value{v, 2} {}
};

Listing 15.3: This is how ‘Date’ uses the new classes.

Book listing lst-0007-book.cpp:

// https://godbolt.org/z/9rY7qhK89 
class Date {
    Year year_;
    Month month_ {1};
    Day day_ {1};
public:
    explicit Date(int y) : year_{y} {} // year-01-01
    Date(Year y, Month m, Day d) : year_{y}, month_{m}, day_{d} {}
    ostream& print(ostream& os) const;
};
ostream& Date::print(ostream& os) const {
    return os << year_ << "-" << month_ << "-"  << day_;
}
ostream& operator<<(ostream& os, const Date& right) {
    return right.print(os);
}
int main() {
    Date d1 { Year{2024}, Month{11}, Day{19} };
    std::cout << d1 << "\n"; // Output: 2024-11-19
}

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

//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/9rY7qhK89 
class Date {
    Year year_;
    Month month_ {1};
    Day day_ {1};
public:
    explicit Date(int y) : year_{y} {} // year-01-01
    Date(Year y, Month m, Day d) : year_{y}, month_{m}, day_{d} {}
    ostream& print(ostream& os) const;
};
ostream& Date::print(ostream& os) const {
    return os << year_ << "-" << month_ << "-"  << day_;
}
ostream& operator<<(ostream& os, const Date& right) {
    return right.print(os);
}
int main() {
    Date d1 { Year{2024}, Month{11}, Day{19} };
    std::cout << d1 << "\n"; // Output: 2024-11-19
}

Listing 15.4: Now “easter” is a method of “Year”.

Book listing lst-0008-book.cpp:

// https://godbolt.org/z/zqKvMedz7 (full example)
class Date;                         // forward declaration
class Year : public Value {
public:
    explicit Year(int v) : Value{v, 4} {}
    Date easter() const;            // declare new method
};
// Declare Month, Day, and Date here. Then:
Date Year::easter() const {         // define new method
    const int y = value_;
    int a = value_/100*1483 - value_/400*2225 + 2613;
    int b = (value_%19*3510 + a/25*319)/330%29;
    b = 148 - b - (value_*5/4 + a - b)%7;
    return Date{Year{value_}, Month{b/31}, Day{b%31 + 1}};
}
int main() {
    using std::cout;
    Year year{2025};
    cout << year.easter() << "\n";  // Output: 2025-04-20
}

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

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/zqKvMedz7 (full example)
class Date;                         // forward declaration
class Year : public Value {
public:
    explicit Year(int v) : Value{v, 4} {}
    Date easter() const;            // declare new method
};
// Declare Month, Day, and Date here. Then:
Date Year::easter() const {         // define new method
    const int y = value_;
    int a = value_/100*1483 - value_/400*2225 + 2613;
    int b = (value_%19*3510 + a/25*319)/330%29;
    b = 148 - b - (value_*5/4 + a - b)%7;
    return Date{Year{value_}, Month{b/31}, Day{b%31 + 1}};
}
int main() {
    using std::cout;
    Year year{2025};
    cout << year.easter() << "\n";  // Output: 2025-04-20
}

Listing 15.5: All components have a white color; only the button will be gray.

Book listing lst-0009-book.cpp:

struct Component {
    Color getColor() const { return white; }
};
struct Window : public Component { };
struct MainWindow : public Window { };
struct Dialog : public Window { };
struct TextInput : public Component { };
struct Button : public Component {
    Color getColor() const  { return gray; }
};

Listing 15.6: What does “print” output? The “value” method appears more often.

Book listing lst-0010-book.cpp:

// https://godbolt.org/z/YTK8PfcMc
#include <iostream>
struct Base {
    int eight_ = 8;
    int value() const { return eight_; }
    void print(std::ostream& os) const { os << value() << "\n"; }
};
struct Print : public Base {
    int nine_ = 9;
    void print(std::ostream& os) const { os << value() << "\n"; }
};
struct Value : public Base {
    int ten_ = 10;
    int value() const { return ten_; }
};
struct Both : public Base {
    int eleven_ = 11;
    int value() const { return eleven_; }
    void print(std::ostream& os) const { os << value() << "\n"; }
};
int main() {
    Base ba{}; ba.print(std::cout);   // Base call
    Print pr{}; pr.print(std::cout);  // print overridden
    Value va{}; va.print(std::cout);  // print from Base
    Both bo{}; bo.print(std::cout);   // everything overridden
}

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

//#(compile) c++; compiler:g141; options:; libs:-
// https://godbolt.org/z/YTK8PfcMc
#include <iostream>
struct Base {
    int eight_ = 8;
    int value() const { return eight_; }
    void print(std::ostream& os) const { os << value() << "\n"; }
};
struct Print : public Base {
    int nine_ = 9;
    void print(std::ostream& os) const { os << value() << "\n"; }
};
struct Value : public Base {
    int ten_ = 10;
    int value() const { return ten_; }
};
struct Both : public Base {
    int eleven_ = 11;
    int value() const { return eleven_; }
    void print(std::ostream& os) const { os << value() << "\n"; }
};
int main() {
    Base ba{}; ba.print(std::cout);   // Base call
    Print pr{}; pr.print(std::cout);  // print overridden
    Value va{}; va.print(std::cout);  // print from Base
    Both bo{}; bo.print(std::cout);   // everything overridden
}

Listing 15.7: Methods marked with “virtual” are resolved at runtime.

Book listing lst-0011-book.cpp:

// https://godbolt.org/z/vW5s3Kh6M 
#include <iostream>

using std::ostream; using std::cout;
struct Base2 {
    int eight_ = 8;
    virtual int value() const          // virtual method
        { return eight_; }
    void print(ostream& os) const
        { os << value() << "\n"; }
};
struct Value2 : public Base2 {
    int ten_ = 10;
    virtual int value() const override // override
        { return ten_; }
};
int main() {
    Value2 v2{}; v2.print(cout);       // use
}

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

//#(compile) c++; compiler:g141; options:; libs:-
// https://godbolt.org/z/vW5s3Kh6M 
#include <iostream>

using std::ostream; using std::cout;
struct Base2 {
    int eight_ = 8;
    virtual int value() const          // virtual method
        { return eight_; }
    void print(ostream& os) const
        { os << value() << "\n"; }
};
struct Value2 : public Base2 {
    int ten_ = 10;
    virtual int value() const override // override
        { return ten_; }
};
int main() {
    Value2 v2{}; v2.print(cout);       // use
}

Listing 15.8: The derived class inherits methods but not constructors from the parent class.

Book listing lst-0013-book.cpp:

// https://godbolt.org/z/59WKfxenE 
class Base {
public:
    Base() {}                 // null-argument constructor
    explicit Base(int i) {}   // one argument
    Base(int i, int j) {}     // two arguments
    void func() {};           // method
};

class Derived : public Base { // no own constructor
};

int main() {
    Base b0{};                // okay, null-argument constructor
    Base b1{12};              // okay, one argument
    Base b2{6,18};            // okay, two arguments
    Derived d0{};             // okay, compiler generates default constructor
    d0.func();                // okay, method is inherited
    Derived d1{7};            // (ERR) Error: no constructor for one argument
    Derived d2{3,13};         // (ERR) Error: no constructor for two arguments
}

Godbolt Listing lst-0013-godb.cpp, https://godbolt.org/z/59WKfxenE:

//#(compile) c++; compiler:g141; options:; libs:-
// https://godbolt.org/z/59WKfxenE 
class Base {
public:
    Base() {}                 // null-argument constructor
    explicit Base(int i) {}   // one argument
    Base(int i, int j) {}     // two arguments
    void func() {};           // method
};

class Derived : public Base { // no own constructor
};

int main() {
    Base b0{};                // okay, null-argument constructor
    Base b1{12};              // okay, one argument
    Base b2{6,18};            // okay, two arguments
    Derived d0{};             // okay, compiler generates default constructor
    d0.func();                // okay, method is inherited
    Derived d1{7};            // (ERR) Error: no constructor for one argument
    Derived d2{3,13};         // (ERR) Error: no constructor for two arguments
}

Listing 15.9: Using “using” to import all parent class constructors.

Book listing lst-0014-book.cpp:

// https://godbolt.org/z/h15eGahjh 
class Base {
public:
    Base() {}
    explicit Base(int i) {}
    Base(int i, int j) {}
    void func() {};           // method
};

class Derived : public Base {
public:
    using Base::Base;         // importing all parent class constructors
};

int main() {
    Derived d0{};             // okay, imported, no longer generated
    Derived d1{7};            // okay, was imported
    Derived d2{3,13};         // okay, was imported
}

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

//#(compile) c++; compiler:g141; options:; libs:-
// https://godbolt.org/z/h15eGahjh 
class Base {
public:
    Base() {}
    explicit Base(int i) {}
    Base(int i, int j) {}
    void func() {};           // method
};

class Derived : public Base {
public:
    using Base::Base;         // importing all parent class constructors
};

int main() {
    Derived d0{};             // okay, imported, no longer generated
    Derived d1{7};            // okay, was imported
    Derived d2{3,13};         // okay, was imported
}

Listing 15.10: Passing by value only copies the common part of the type.

Book listing lst-0015-book.cpp:

// https://godbolt.org/z/Ec7vqEdY3 
//… Base2 and Value2 as before …
void output(Base2 x) {          // pass by value
    x.print(cout);
}
int main() {
    Base2 ba2{}; output(ba2);   // outputs 8
    Value2 va2{}; output(va2);  // also outputs 8
}

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

//#(compile) c++; compiler:g141; options:; libs:-
// https://godbolt.org/z/Ec7vqEdY3 
//… Base2 and Value2 as before …
void output(Base2 x) {          // pass by value
    x.print(cout);
}
int main() {
    Base2 ba2{}; output(ba2);   // outputs 8
    Value2 va2{}; output(va2);  // also outputs 8
}

Listing 15.11: The derived class as an argument type to a function does not allow calling with a base class variable as a value parameter.

Book listing lst-0016-book.cpp:

// https://godbolt.org/z/17nah59eP
//… Base2 and Value2 as before …
void output(Value2 x) {         // derived class as value
    x.print(cout);
}
int main() {
    Base2 ba2{}; output(ba2);   // (ERR) ba2 cannot be converted to Value2
    Value2 va2{}; output(va2);  // outputs 10
}

Godbolt Listing lst-0016-godb.cpp, https://godbolt.org/z/17nah59eP:

//#(compile) c++; compiler:g141; options:; libs:-
// https://godbolt.org/z/17nah59eP
//… Base2 and Value2 as before …
void output(Value2 x) {         // derived class as value
    x.print(cout);
}
int main() {
    Base2 ba2{}; output(ba2);   // (ERR) ba2 cannot be converted to Value2
    Value2 va2{}; output(va2);  // outputs 10
}

Listing 15.12: Passing by reference does not change the instance.

Book listing lst-0017-book.cpp:

// https://godbolt.org/z/sW7MexWrh 
//… Base2 and Value2 as before …
void output(Base2& x) {       // passing as a reference
    x.print(cout);
}

int main() {
    Base2 ba2{}; output(ba2);  // outputs 8
    Value2 va2{}; output(va2); // outputs 10, because the object is not copied
}

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

//#(compile) c++; compiler:g141; options:; libs:-
// https://godbolt.org/z/sW7MexWrh 
//… Base2 and Value2 as before …
void output(Base2& x) {       // passing as a reference
    x.print(cout);
}

int main() {
    Base2 ba2{}; output(ba2);  // outputs 8
    Value2 va2{}; output(va2); // outputs 10, because the object is not copied
}