I want to teach about encapsulation and chose Date with Year, Month and Day as an example -- because it demonstrates type-safety w.r.t. preventing accidental swapping of parameters. I want to demonstrate encapusulation to the extreme, meaning I want to hide the int-values of the Year, Month and Day completely and instead define the operations in them as required.Disregarding if it's good to go to this extreme when encapsulating, does anyone have any comments about my demonstration code?Intro section// #!cpp filename=33a-dateplus.cpp#include <iostream>#include <iomanip>using std::ostream; using std::setfill; using std::setw;Helper value classesHelper value class Yearclass Year {    int value_; // eg. 2014public:    explicit Year(int v) : value_{v} {}    Year& operator+=(const Year& other) {        value_ += other.value_;        return *this;    }    friend ostream& operator<<(ostream& os, const Year&x) {        return os << setfill('0') << setw(4) << x.value_;    }    bool isLeap() const;};Helper value class Monthclass Day;class Month {    int value_; // 1..12public:    // v may be invalid month-number, to be normalized later, but >0 .    explicit Month(int v) : value_{v} {}    Month& operator+=(const Month& other) {        value_ += other.value_;        return *this;    }    friend ostream& operator<<(ostream& os, const Month&x) {        return os << setfill('0') << setw(2) << x.value_;    }    void normalize(Year &year);    // precond: month must be normalized; value_ in [1..12]    Day days(const Year& inYear) const;    friend bool operator<(const Month &l, const Month& r) {        return l.value_ < r.value_;    }};Helper value class Dayclass Day {    int value_; // 1..31public:    // v may be invalid day-of-month, to be normalized later, but >0 .    explicit Day(int v) : value_{v} {}    Day& operator+=(const Day& other) {        value_ += other.value_;        return *this;    }    Day& operator-=(const Day& other) {        value_ -= other.value_;        return *this;    }    friend bool operator<(const Day& l, const Day& r) {        return l.value_ < r.value_;    }    void normalize(Month& month, Year& year);    friend ostream& operator<<(ostream& os, const Day&x) {        return os << setfill('0') << setw(2) << x.value_;    }};Date, the class we are mainly designingclass 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} {}    friend ostream& operator<<(ostream& os, const Date&x) {        return os << x.year_ << - << x.month_ << -  << x.day_;    }    // add an arbitrary number of days to a date; normalizez afterwards    friend Date operator+(Date date, const Day& day) {        date.day_ += day;        date.normalize(); // handle overflows        return date;    }    void normalize();};Implementing member functionsbool Year::isLeap() const {    return ( (value_%4==0) && (value_%100!=0) ) || (value_%400==0);}Day Month::days(const Year& inYear) const {    switch(value_) {    case 1: case 3: case 5: case 7: case 8: case 10: case 12:        return Day{31};    case 4: case 6: case 9: case 11:        return Day{30};    case 2:        return inYear.isLeap() ? Day{29} : Day{28};    }    return Day{0}; // invalid value_}Normalization functionsvoid Month::normalize(Year &year) {    if(12 < value_ || value_ < 1) {        auto ival = value_-1; // -1: for [1..12] to [0..11]        year += Year{ ival / 12 };        value_ = value_ % 12 + 1; // +1: back to [1..12]    }}void Day::normalize(Month& month, Year& year) {    // normalize month, adjusting year    month.normalize(year);    // normalize day; adjusting month and year    while(month.days(year) < *this) {        *this -= month.days(year);        month += Month{1};        if(Month{12} < month) {            month = Month{1};            year += Year{1};        }    } }// afterwards contains valid valuesvoid Date::normalize() {    day_.normalize(month_, year_);}A test: mainint main() {    using std::cout;    Date d1 { Year{2013}, Month{15}, Day{199} };    cout << d1 <<  = ;    d1.normalize();    cout << d1 << \n;    for(auto yi : {1898, 1899, 1900, 1901,            1998, 1999, 2000, 2001, 2002, 2003, 2004}) {        Date d { Year{yi}, Month{3}, Day{366} };        cout << d <<  = ;        d.normalize();        cout << d << \n;    }    for(auto yi : {2011, 2012, 2013, 2014}) {        Date d { Year{yi}, Month{2}, Day{1} };        cout << d <<  +28d =  << d+Day{28} << \n;    }}NotesThe code is supposed to follow a modern programming style, which here means:C++11: use of {...} for initialization in most cases, autotype-safety, esp. no evil castsa bit more use of class-instances a values, i.e. value-semanticsOutput2013-15-199 = 2014-10-161898-03-366 = 1899-03-011899-03-366 = 1900-03-011900-03-366 = 1901-03-011901-03-366 = 1902-03-011998-03-366 = 1999-03-011999-03-366 = 2000-02-292000-03-366 = 2001-03-012001-03-366 = 2002-03-012002-03-366 = 2003-03-012003-03-366 = 2004-02-292004-03-366 = 2005-03-012011-02-01 +28d = 2011-03-012012-02-01 +28d = 2012-02-292013-02-01 +28d = 2013-03-012014-02-01 +28d = 2014-03-01
Type-safe Date class with total encapsulation
c++;c++11;datetime;type safety