Friday, 8 June 2018

Chapter 9 // Exercise 5 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 5



This exercise and the next few require you to design and implement a Book class, such as you can imagine as part of software for a library. Class Book should have members for the ISBN, title, author, and copyright date. Also store data on whether or not the book is checked out. Create functions for returning those data values. Create functions for checking a book in and out. Do simple validation of data entered into a Book; for example, accept ISBNs only if the form n-n-n-x where n is an integer and x is a digit or letter. Store an ISBN as a string.


#include "stdafx.h"
#include "std_lib_facilities.h"

class Book
{
public:
 Book();
 ~Book();

 string getISBN()   { return m_isbn; }
 string getTitle()  { return m_title; }
 string getAuthor() { return m_author; }
 int    getDate()   { return m_date; }
 bool   getStatus() { return m_checkedOut; }

 void setISBN(string num1, string num2, string num3, string char1);
 void setTitle(string title) { m_title = title; }
 void setAuthor(string author) { m_author = author; }
 void setDate(string year);
 
 void checkBookOut() { m_checkedOut = true; };
 void checkBookIn() { m_checkedOut = false; };

 void printStats();

private:
 //m_ prefix to denote member variable
 string m_isbn, m_title, m_author;
 int m_date;
 bool m_checkedOut;
};

//constructor
Book::Book()
{
 m_isbn = m_title = m_author = "";
 m_date = 0;
 m_checkedOut = false;
}

//deconstructor
Book::~Book() {}

//set the ISBN
void Book::setISBN(string num1, string num2, string num3, string char1)
{
 size_t check;
 //check first number
 check = num1.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num1;
  check = num1.find_first_not_of("0123456789");
 }

 //check second number
 check = num2.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num2;
  check = num2.find_first_not_of("0123456789");
 }

 //check third number
 check = num3.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num3;
  check = num3.find_first_not_of("0123456789");
 }

 //check last character
 check = char1.find_first_not_of("0123456789ABCDEFGHIJKLMNOPQRSTUVWXY");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN digit or letter, try again: ";
  cin >> char1;
  check = char1.find_first_not_of("0123456789ABCDEFGHIJKLMNOPQRSTUVWXY");
 }

 //set isbn
 m_isbn = "";
 m_isbn.append(num1);
 m_isbn.append("-");
 m_isbn.append(num2);
 m_isbn.append("-");
 m_isbn.append(num3);
 m_isbn.append("-");
 m_isbn.append(char1);
}

//set and check the year of copyright
void Book::setDate(string year)
{
 size_t check;
 check = year.find_first_not_of("0123456789");
 while (check != string::npos || year.size() != 4)
 {
  cout << "Not a valid year try again: ";
  cin >> year;
  check = year.find_first_not_of("0123456789");
 }

 m_date = stoi(year);
}

//print book details
void Book::printStats()
{
 cout << "Title: " << m_title << endl;
 cout << "Author: " << m_author << endl;
 cout << "Copyright: " << m_date << endl;
 cout << "ISBN: " << m_isbn << endl;
 if (m_checkedOut)
  cout << "Checked out: Yes" << endl;
 else
  cout << "Checked out: No" << endl;

 cout << endl;
}


int main()
{
 Book lotr;
 lotr.setTitle("Lord Of The Rings");
 lotr.setAuthor("J.R.R Tolkien");
 lotr.setDate("1954");
 lotr.setISBN("1111", "1111", "1111", "J");
 lotr.printStats();

 lotr.setISBN("123", "0", "4444", "6");
 lotr.checkBookOut();
 lotr.printStats();

 keep_window_open();

 return 0;
}


This was quite a confusing task as many assumptions had to be made. First, what kind of copyright date should be set? Generally the day and month are optional, so I decided to only accept the year of copyright. Second, how many integers could be allowed for the ISBN format? I'm not sure if he meant only 1 number or however many. For example 1-1-1-H, or 11111-45676543-4-7. I decided to go with the latter.

The hardest part was the check for the ISBN, however whilst searching how to solve turning a string into an int, I came across a method of std::string called 'find_first_not_of' which handily takes in a string of letters that you want included and then returns the position of the first letter not wanted. This made checking the ISBN parts easy, as the while is set to check if there is a position or not. If there is one, then an undesired character was entered, if there is no position then there were no unwanted characters. A very handy method.

Tuesday, 5 June 2018

Chapter 9 // Exercise 4 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 4


Look at the headache-inducing last example of section 8.4. Indent it properly and explain the meaning of each construct. Note that the example does do anything meaningful; it is pure obfuscation.

#include "stdafx.h"
#include "std_lib_facilities.h"

//just ew
struct X
{
 //strangely returns 1 no matter what
 void f(int x)
 {
  struct Y
  {
   int f() { return 1; }
   int m;
  };

  int m;
  m = x; 
  Y m2;
  return f(m2.f());
 }

 int m;
 //if m, return 3 if not return whatever is in m + 2
 void g(int m)
 {
  if (m)
   f(m + 2);
  else
  {
   g(m + 2);
  }
 }

 X() {} //default constructor for X struct
 void m3() {} //define and declare m3, basically does nothing
 void main()
 {
  X a;
  a.f(2);  //return 1
 }

};

This code is so confusing that even though it doesn't do anything it still made me question myself. 

Sunday, 3 June 2018

Chapter 9 // Exercise 3 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 3



Replace Name_pair::print() with a (global) operator <<  and define  ==  and !=  for Name_pairs.

#include "stdafx.h"
#include "std_lib_facilities.h"

class Name_pairs
{
private:
 vector<string> name;
 vector<double> age;
public:
 void read_names(int iterator);  //read in a series of names
 void read_ages();     //read in age for name
 void sortNP();      //sort the vectors alphabetically by name
 vector<string> getNames() { return name; }
 vector<double> getAges() { return age; }
};

ostream& operator<<(ostream& os, Name_pairs& n) //print the vectors
{
 cout << endl;
 for (int i = 0; i < n.getNames().size(); ++i)
  os << "Name: " << n.getNames()[i] << "     Age: " << n.getAges()[i] << endl;
 return os;
}
//equals
bool operator==(Name_pairs& a, Name_pairs& b)
{
 return a.getNames() == b.getNames() &&
  a.getAges() == b.getAges();
}

//not equals
bool operator!=(Name_pairs& a, Name_pairs& b)
{
 return !(a == b);
}
void Name_pairs::read_names(int iterator)
{
 string names;
 for (int i = 0; i < iterator; ++i)
 {
  cout << "Name: ";
  cin >> names;
  name.push_back(names);
  cout << endl;
 }
}

void Name_pairs::read_ages()
{
 double ages;
 for (int i = 0; i < name.size(); ++i)
 {
  cout << "Age for " << name[i] << ": ";
  cin >> ages;
  age.push_back(ages);
  cout << endl;
 }
}

void Name_pairs::sortNP()
{
 vector<string> name_copy = name;
 vector<double> age_copy = age;

 sort(name.begin(), name.end());  //sort actual names

 //go through each member of sorted name
 for (int i = 0; i < name.size(); ++i)
 {
  //go through each member of original copy name
  for (int j = 0; j < name.size(); ++j)
  {
   //if sorted name matches original
   if (name[i] == name_copy[j])
   {
    //assign original age to new position to match sorted vector
    age[i] = age_copy[j];
   }
  }
 }
}

int main()
{
 cout << "How many names to read in? > ";
 int howMany;
 cin >> howMany;

 Name_pairs namePair;

 //read in names
 namePair.read_names(howMany);

 //read in ages
 namePair.read_ages();

 //print
 cout << namePair;

 //sort namePair alphabetically
 namePair.sortNP();

 //print
 cout << namePair;

 keep_window_open();

 return 0;
}


The most challenging part of this was getting the ostream operator to work properly. At the moment he's only shown examples for having it outside of the class but I'm pretty sure there is a way to have it inside the class as a member.

Also, he didn't particularly specify what type of == and != he wanted. Was to compare the name_pair in general or to compare specific days? I did it generally, however it could be improved to compare specific days and ages.

Friday, 1 June 2018

Chapter 9 // Exercise 2 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 1


Design and implement a Name_pairs class holding (name, age) pairs where name is a string and age is a double. Represent that as a vector<string>  (called name) and a vector<double> (called age) member. Provide an input operation read_names() that reads a series of names. Provide a read_ages() operation that prompts the user for an age for each name. Provide a print() operation that prints out the (name[i], age[i]) pairs (one per line) in the order determined by the name vector. Provide a sort() operation that sorts the name vector in alphabetical order and reorganises the age vector to match. Implement all "operations" as member functions.

#include "stdafx.h"
#include "std_lib_facilities.h"

class Name_pairs
{
private:
 vector<string> name;
 vector<double> age;
public:
 void read_names(int iterator); //read in a series of names
 void read_ages();  //read in age for name
 void print();   //print out vectors
 void sortNP();   //sort the vectors alphabetically by name
};

void Name_pairs::read_names(int iterator)
{
 string names;
 for (int i = 0; i < iterator; ++i)
 {
  cout << "Name: ";
  cin >> names;
  name.push_back(names);
  cout << endl;
 }
}

void Name_pairs::read_ages()
{
 double ages;
 for (int i = 0; i < name.size(); ++i)
 {
  cout << "Age for " << name[i] << ": ";
  cin >> ages;
  age.push_back(ages);
  cout << endl;
 }
}

void Name_pairs::print()
{
 cout << endl;
 for (int i = 0; i < name.size(); ++i)
  cout << "Name: " << name[i] << "     Age: " << age[i] << endl;
}

void Name_pairs::sortNP()
{
 vector<string> name_copy = name;
 vector<double> age_copy = age;

 sort(name.begin(), name.end());  //sort actual names

 //go through each member of sorted name
 for (int i = 0; i < name.size(); ++i)
 {
  //go through each member of original copy name
  for (int j = 0; j < name.size(); ++j)
  {
   //if sorted name matches original
   if (name[i] == name_copy[j])
   {
    //assign original age to new position to match sorted vector
    age[i] = age_copy[j];
   }
  }
 }
}

int main()
{
 cout << "How many names to read in? > ";
 int howMany;
 cin >> howMany;

 Name_pairs namePair;

 //read in names
 namePair.read_names(howMany);

 //read in ages
 namePair.read_ages();

 //print
 namePair.print();

 //sort namePair alphabetically
 namePair.sortNP();

 //print
 namePair.print();

 keep_window_open();

 return 0;
}

This was basically chapter 8 exercise 7 but in a class form, so I just reused some of the functions from that and stuck it into a class.

Wednesday, 16 May 2018

Chapter 9 // Drill 1, 2, 3, 4, 5 - Principles & Practice Using C++

n this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


EDIT 24/03/2021
Please note the code below is for historical purposes (it's quite wrong). Also, thanks to a comment below I noticed two issues in add_day and add_month. The changes can be found here:
I haven't changed the other drills. Also, as the drill says the check for a valid date can be very simple, I didn't check for invalid dates like certain months only having 30 days or 31. It basically acts as though every month is 31 days long.

EDIT 31/10/2019
My lord, what a mess these examples are. I have now updated these drills on my GitHub as I work my way through the book again. Even though I only did this a year ago, I can't believe how much my skills have improved (as well as my ability to use c style casts...I use them too much now). Seeing the whole bool last year, end year if not last year = ...eurgh.

Chapter 9 // Drill



This drill simply involves getting the sequence of versions of Date to work. For each version define a Date called today initialised to June 25, 1978. Then, define a Date called tomorrow and give is a value by copying today into it and increasing its day by one using add_day(). Finally output today and tomorrow using a << defined as in section 9.8.

Your check for a valid date may be very simple. Feel free to ignore leap years. However, don't accept a month that is not in the (1,12) range or day of the month that is not in the (1,31) range. Test each version with at least one invalid date (e.g., 2004, 13, -5).

Drill 1
The version from section 9.4.1:

#include "stdafx.h"
#include "std_lib_facilities.h"

//9.4.1 - simple Date
struct Date
{
 int y;  //year
 int m;  //month
 int d;  //day
};

Date today;
Date tomorrow;

//initialise the day
void init_day(Date& dd, int y, int m, int d)
{
 //check that y m d is a valid date
 if (y < 1900 || y > 2018)
  cout << "Error, invalid year." << endl;
 else if (m < 1 || m > 12)
  cout << "Error, invalid month." << endl;
 else if (d < 1 || d > 31)
  cout << "Error, invalid day." << endl;
 else
 {
  //if date is valid, initalise the date
  dd.y = y;
  dd.m = m;
  dd.d = d;
 }

 return;
}

//increment date by n days
void add_day(Date dd, Date& dd_copy, int n)
{
 bool lastDay = false;
 bool endYear = false;

 //if day go above 31, increase month, set day to 1
 //if month goes above 12, increase year, set month to 1
 for (int i = 0; i < n; ++i)
 {
  //wrap days
  if (dd.d == 31)
   lastDay = true;
  dd.d = (dd.d == 31) ? 1 : ++dd.d;

  if (lastDay)
  {
   //wrap month
   lastDay = false;
   dd.m = (dd.m == 12) ? 1 : ++dd.m;
   if (dd.m == 12)
    endYear = true;

   if (endYear)
   {
    //just increase year by one
    endYear = false;
    ++dd.y;
   }

  }
  
 }

 //assign tomorrow with copy of modified today
 dd_copy = dd;
}

//print to screen
ostream& operator<<(ostream& os, const Date& d)
{
 return os << d.d << ", " << d.m << ", " << d.y << endl;
}

int main()
{
 //initialise today with June 25 1978
 init_day(today, 1978, 6, 25);

 //assign by copying and increase by 1 day
 add_day(today, tomorrow, 1);

 //print out results
 cout << "Today: " << today << endl;
 cout << "Tomorrow: " << tomorrow << endl;

 keep_window_open();

 return 0;
}

The most challenging part of this was getting the days and months to wrap correctly. I know he only wanted us to increase by 1 day but I feel like he's going to ask us to do something similar at some point. The days only wrap at 31 though so it's not completely accurate however it does work as expected.

Drill 2

The version from section 9.4.2:


#include "stdafx.h"
#include "std_lib_facilities.h"

//9.4.2 - simple Date
//guarantee initialisation with constructor
//provide some notational convenience
struct Date
{
 int y, m, d;   //year, month, day
 Date(int y, int m, int d); //check for valid date and initialise
 void add_day(int n);  //increase the Date by N days
};

//intialise dates
Date today(1978, 6, 25);
Date tomorrow(today);

//Date Constructor - initialise the day
Date::Date(int y, int m, int d)
{
 //check that y m d is a valid date
 if (y < 1900 || y > 2018)
  cout << "Error, invalid year." << endl;
 else if (m < 1 || m > 12)
  cout << "Error, invalid month." << endl;
 else if (d < 1 || d > 31)
  cout << "Error, invalid day." << endl;
 else
 {
  //if date is valid, initalise the date
  Date::y = y;
  Date::m = m;
  Date::d = d;
 }

 return;
}

//increment date by n days
void Date::add_day(int n)
{
 bool lastDay = false;
 bool endYear = false;

 //if day goes above 31, increase month, set day to 1
 //if month goes above 12, increase year, set month to 1
 for (int i = 0; i < n; ++i)
 {
  //wrap days
  if (Date::d == 31)
   lastDay = true;
  Date::d = (Date::d == 31) ? 1 : ++Date::d;  //if day is equal to 31, make it 1, otherwise ++

  if (lastDay)
  {
   //wrap month
   lastDay = false;
   Date::m = (Date::m == 12) ? 1 : ++Date::m; //if month is equal to 12, make it 1, otherwise ++
   if (Date::m == 12)
    endYear = true;

   if (endYear)
   {
    //just increase year by one
    endYear = false;
    ++Date::y;
   }

  }
  
 }
}

//print to screen
ostream& operator<<(ostream& os, const Date& d)
{
 return os << d.d << ", " << d.m << ", " << d.y << endl;
}

int main()
{
 //increase day by one
 tomorrow.add_day(1);

 //print out results
 cout << "Today: " << today << endl;
 cout << "Tomorrow: " << tomorrow << endl;

 keep_window_open();

 return 0;
}
For this version, the two functions were changed a little to become part of the Date struct and used the global operator to assign straight to the variables in the struct instead of taking in variables. Tomorrow is also intialised with today and then increased in main() using the add_day() function.
Drill 3
The version from section 9.4.3:
#include "stdafx.h"
#include "std_lib_facilities.h"

#include "stdafx.h"
#include "std_lib_facilities.h"

//9.4.3 - simple Date (control access)
class Date
{
private:
 int y, m, d;
public:
 Date(int y, int m, int d);
 void add_day(int n);

 int month() { return m; }
 int day() { return d; }
 int year() { return y; }
};

//intialise dates
Date today(1978, 6, 25);
Date tomorrow(today);

//Date Constructor - initialise the day
Date::Date(int y, int m, int d)
{
 //check that y m d is a valid date
 if (y < 1900 || y > 2018)
  cout << "Error, invalid year." << endl;
 else if (m < 1 || m > 12)
  cout << "Error, invalid month." << endl;
 else if (d < 1 || d > 31)
  cout << "Error, invalid day." << endl;
 else
 {
  //if date is valid, initalise the date
  Date::y = y;
  Date::m = m;
  Date::d = d;
 }

 return;
}

//increment date by n days
void Date::add_day(int n)
{
 bool lastDay = false;
 bool endYear = false;

 //if day goes above 31, increase month, set day to 1
 //if month goes above 12, increase year, set month to 1
 for (int i = 0; i < n; ++i)
 {
  //wrap days
  if (Date::d == 31)
   lastDay = true;
  Date::d = (Date::d == 31) ? 1 : ++Date::d;  //if day is equal to 31, make it 1, otherwise ++

  if (lastDay)
  {
   //wrap month
   lastDay = false;
   Date::m = (Date::m == 12) ? 1 : ++Date::m; //if month is equal to 12, make it 1, otherwise ++
   if (Date::m == 12)
    endYear = true;

   if (endYear)
   {
    //just increase year by one
    endYear = false;
    ++Date::y;
   }

  }
  
 }
}

//print to screen
ostream& operator<<(ostream& os, Date& d)
{
 return os << d.day() << ", " << d.month() << ", " << d.year() << endl;
}

int main()
{
 //increase day by one
 tomorrow.add_day(1);

 //print out results
 cout << "Today: " << today << endl;
 cout << "Tomorrow: " << tomorrow << endl;

 keep_window_open();

 return 0;
}
Not much changed on this version, just the operator overload which could no longer access the variables within the class directly. It had to be changed to non-const though as it kept returning an error which I found strange considering I wasn't trying to change anything; just print it to the screen.

Drill 4
The version from section 9.7.1:
#include "stdafx.h"
#include "std_lib_facilities.h"

enum class Month
{
 jan = 1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
};

//forward declaration
Month returnMonth(int month);

//9.4.3 - simple Date (use Month type)
class Date
{
public:
 Date(int y, Month m, int d);
 void add_day(int n);
 int year() { return y; }
 Month month() { return m; }
 int day() { return d; }
private:
 int y;
 Month m;
 int d;
};

//intialise dates
Date today(1978, Month::jun, 25);
Date tomorrow(today);

//Date Constructor - initialise the day
Date::Date(int y, Month m, int d)
{
 //check that y m d is a valid date
 if (y < 1900 || y > 2018)
  cout << "Error, invalid year." << endl;
 else if (static_cast<int>(m) < 1 || static_cast<int>(m) > 12)
  cout << "Error, invalid month." << endl;
 else if (d < 1 || d > 31)
  cout << "Error, invalid day." << endl;
 else
 {
  //if date is valid, initalise the date
  Date::y = y;
  Date::m = m;
  Date::d = d;
 }

 return;
}

//increment date by n days
void Date::add_day(int n)
{
 bool lastDay = false;
 bool endYear = false;

 //if day goes above 31, increase month, set day to 1
 //if month goes above 12, increase year, set month to 1
 for (int i = 0; i < n; ++i)
 {
  //wrap days
  if (Date::d == 31)
   lastDay = true;
  Date::d = (Date::d == 31) ? 1 : ++Date::d;  //if day is equal to 31, make it 1, otherwise ++

  if (lastDay)
  {
   //wrap month
   lastDay = false;
   int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1); //if month is equal to 12, make it 1, otherwise ++
   Date::m = returnMonth(mon);
   if (static_cast<int>(Date::m) == 12)
    endYear = true;

   if (endYear)
   {
    //just increase year by one
    endYear = false;
    ++Date::y;
   }

  }
  
 }
}

//switch to return correct type of Month
Month returnMonth(int month)
{
 switch (month)
 {
 case 1:
  return Month::jan;
  break;
 case 2:
  return Month::feb;
  break;
 case 3:
  return Month::mar;
  break;
 case 4:
  return Month::apr;
  break;
 case 5:
  return Month::may;
  break;
 case 6:
  return Month::jun;
  break;
 case 7:
  return Month::jul;
  break;
 case 8:
  return Month::aug;
  break;
 case 9:
  return Month::sep;
  break;
 case 10:
  return Month::oct;
  break;
 case 11:
  return Month::nov;
  break;
 case 12:
  return Month::dec;
  break;
 default:
  cout << "Bad month" << endl;
 }
}

//print to screen
ostream& operator<<(ostream& os, Date& d)
{
 return os << d.day() << ", " << static_cast<int>(d.month()) << ", " << d.year() << endl;
}

int main()
{
 //increase day by one
 tomorrow.add_day(10);

 //print out results
 cout << "Today: " << today << endl;
 cout << "Tomorrow: " << tomorrow << endl;

 keep_window_open();

 return 0;
}
There is actually 2 versions of Date in this section and he doesn't specify which one to use so however the second one includes a second class inside of it called Invalid{} which he hasn't hinted at anywhere so I went with the first. I'm guessing it was an error check but why not just make it a function? The fact that it was a class denotes a few functions in there so I'm not going to guess what they may be.

Anyway, because of enums being special I had to create a quick function which could assign the correct month in the add_day() function as you can't assign an integer to an enum. Well, you can, but some very strange things happen and even then the enum doesn't know what that integer means. For example say we wanted to assign June to our month variable, you can't just "=6" as the enum doesn't know that 6 is June. So the function uses a switch statement to compare the enum against an integer and then return the correct value.


Drill 5
The version from section 9.7.4:
#include "stdafx.h"
#include "std_lib_facilities.h"

enum class Month
{
 jan = 1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
};

//forward declaration
Month returnMonth(int month);

//9.4.3 - simple Date (use Month type)
class Date
{
public:
 Date(int y, Month m, int d);
 int day() const { return d; };  //const member: can't modify
 Month month() const { return m; }
 int year() const { return y; }

 void add_day(int n);
 void add_month(int n);
 void add_year(int n);

 bool lastDay = false;
 bool endYear = false;
private:
 int y;
 Month m;
 int d;
};

//intialise dates
Date today(1978, Month::jun, 25);
Date tomorrow(today);

//Date Constructor - initialise the day
Date::Date(int y, Month m, int d)
{
 //check that y m d is a valid date
 if (y < 1900 || y > 2018)
  cout << "Error, invalid year." << endl;
 else if (static_cast<int>(m) < 1 || static_cast<int>(m) > 12)
  cout << "Error, invalid month." << endl;
 else if (d < 1 || d > 31)
  cout << "Error, invalid day." << endl;
 else
 {
  //if date is valid, initalise the date
  Date::y = y;
  Date::m = m;
  Date::d = d;
 }

 return;
}

//increment date by n days
void Date::add_day(int n)
{
 //if day goes above 31, increase month, set day to 1
 //if month goes above 12, increase year, set month to 1
 for (int i = 0; i < n; ++i)
 {
  //wrap days
  if (Date::d == 31)
   lastDay = true;
  Date::d = (Date::d == 31) ? 1 : ++Date::d;  //if day is equal to 31, make it 1, otherwise ++

  if (lastDay)
  {
   //wrap month
   lastDay = false;
   int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1); //if month is equal to 12, make it 1, otherwise ++
   Date::m = returnMonth(mon);
   if (static_cast<int>(Date::m) == 12)
    endYear = true;

   if (endYear)
   {
    //just increase year by one
    endYear = false;
    ++Date::y;
   }

  }
  
 }
}

void Date::add_month(int n)
{
 for (int i = 0; i < n; ++i)
 {
  //if month is equal to 12, make it 1, otherwise ++
  int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1);
  Date::m = returnMonth(mon);
  if (static_cast<int>(Date::m) == 12)
   endYear = true;

  if (endYear)
  {
   //just increase year by one
   endYear = false;
   ++Date::y;
  }
 }
}

void Date::add_year(int n)
{
 for (int i = 0; i < n; ++i)
  ++Date::y;
}

//switch to return correct type of Month
Month returnMonth(int month)
{
 switch (month)
 {
 case 1:
  return Month::jan;
  break;
 case 2:
  return Month::feb;
  break;
 case 3:
  return Month::mar;
  break;
 case 4:
  return Month::apr;
  break;
 case 5:
  return Month::may;
  break;
 case 6:
  return Month::jun;
  break;
 case 7:
  return Month::jul;
  break;
 case 8:
  return Month::aug;
  break;
 case 9:
  return Month::sep;
  break;
 case 10:
  return Month::oct;
  break;
 case 11:
  return Month::nov;
  break;
 case 12:
  return Month::dec;
  break;
 default:
  cout << "Bad month" << endl;
 }
}

//print to screen
ostream& operator<<(ostream& os, Date& d)
{
 return os << d.day() << ", " << static_cast<int>(d.month()) << ", " << d.year() << endl;
}

int main()
{
 //increase day by one
 tomorrow.add_day(1);
 tomorrow.add_month(2);
 tomorrow.add_year(5);

 //print out results
 cout << "Today: " << today << endl;
 cout << "Tomorrow: " << tomorrow << endl;

 keep_window_open();

 return 0;
}

For this one I just separated parts of add_day() into their own functions with a few tweaks.