1. 示例:使用引用的"Time"类

回到之前版本的"Time"类,假设我们想增加链式操作,例如t.nextSecond().nextSecond().print(),我们就需要让nextSecond()返回this的一个引用。

Time.h

class Time {
private:
	......
public:
	Time &nextSecond(); // Return a reference to "this" instance
	......
}

在函数原型中,我们申明了一个nextSecond()的成员函数,返回Time对象的引用。返回的引用可以用来继续调用成员函数。

Time.cpp

// Increase this instance by one second and return this instance by reference.
Time &Time::nextSecond() {
	if (++second == 60) {
		second = 0;
		if (++minute == 60) {
			minute = 0;
			if (++hour = 24) {
				hour = 0;
			}
		}
	}

	return *this; // Return this instance by reference
				  // "this" is a pointer to this instance. *this refers to the instance.
}

C++有一个关键字"this",它保存了一个指向当前实例的指针。也就是说,*this指的是当前实例。

TestTime.cpp

Time t1(23, 59, 58);
t1.print();
t2.nextSecond();
t1.print();
t1.nextSecond().nextSecond().print();
t1.print();

2. 示例:“Time"类的第三个版本,异常处理

输入检验是必要的,例如,在setHour(int h)函数中,我们需要检验输入的参数是 0~23 之间的数字。校验参数很简单,但是如果校验失败的话我们该如何处理错误呢,我们是输出一句错误 提示然后终止程序呢,还是输出一个警告,然后继续执行程序直到错误不能被容忍为止呢?这两种方式都不好。

void Time::setHour(int h) {
	if (h >= 0 && h <= 23) {
		hour = h;
	} else {
		cout << "Error: Invalid hour! Hour shall be 0~23." << endl;
		exit(1);
	}
}
void Time::setHour(int h) {
	if (h >= 0 && h <= 23) {
		hour = h;
	} else {
		cout << "Warning: Invalid hour! Hour shall be 0-23." << endl;
		hour = 0;
	}
}

取而代之的是,C++提供了一种异常处理机制(在头文件),这种机制能够优雅地处理异常。

Time.h

#ifndef TIME_H
#define TIME_H

class Time {
private:
	int hour;
	int minute;
	int second;

public:
	Time(int h = 0, int m = 0, int s = 0);
	int getHour() const;
	void setHour(int h);
	int getMinute() const;
	void setMinute(int m);
	int getSecond() const;
	void setSecond(int s);
	void setTime(int h, int m, int s);
	void print() const;
};

#endif

Time.cpp

#include <iostream>
#include <iomanip>
#include <stdexcept>
#include "Time.h"

using namespace std;

Time::Time(int h, int m, int s) {
	setHour(h);
	setMinute(m);
	setSecond(s);
}

int Time::getHour() const {
	return hour;
}

void Time::setHour(int h) {
	if (h >= 0 && h <= 23) {
		hour = h;
	} else {
		throw invalid_argument("Invalid hour! Hour shall be 0~23.");
	}
}

int Time::getMinute() const {
	return minute;
}

void Time::setMinute(int m) {
	if (m >= 0 && m <= 59) {
		minute = m;
	} else {
		throw invalid_argument("Invalid minute! Minute shall be 0~59.");
	}
}

int Time::getSecond() const {
	return second;
}

void Time::setSecond(int s) {
	if (s >= 0 && s <= 59) {
		second = s;
	} else {
		throw invalid_argument("Invalid second! Second shall be 0~59.");
	}
}

void Time::setTime(int h, int m, int s) {
	setHour(h);
	setMinute(m);
	setSecond(s);
}

void Time::print() const {
	cout << setfill('0');
	cout << setw(2) << hour << ":" << setw(2) << minute << ":"
		<< setw(2) << second << endl;
}

TestTime.cpp

#include <iostream>
#include <stdexcept>

#include "Time.h"

using namespace std;

int main() {
	try {
		Time t1(25, 0, 0);
		t1.print();
	} catch (invalid_argument &ex) {
		cout << "Exception:" << ex.what() << endl;
	}

	cout << "Next statement after try-catch" << endl;
}

3. 对象引用,指针和数组中的动态内存分配(高级)

#include <iostream>
#include "Time.h"
using namespace tsd;

int main() {
	Time t1(1, 2, 3);
	t1.print();

	Time *ptrT1 = &t1;
	(*ptrT1).print();
	ptrT1->print();

	Time &refT1 = t1;
	refT1.print();

	Time *ptrT2 = new Time(4, 5, 6);
	ptrT2->print();
	delete ptrT2;

	Time tArray1[2];
	tArray1[0].print();
	tArray1[1].print();

	Time tArray2[2] = {Time(7, 8, 9), Time(10)};
	tArray2[0].print();
	tArray2[1].print();

	Time *ptrTarray3 = new Time[2];
	ptrTarray3[0].print();
	ptrTarray3[1].print();
	delete[] ptrTarray3;

	// C++11 syntax, compile with -std=c++0x
	Time *ptrTarray4 = new Time[2] {Time(11, 12, 13), Time(14)};
	ptrTarray4->print();
	(ptrTarray4 + 1)->print();
	delete[] ptrTarray4;
}

4. 示例:复数类

下面是复数类的类图

Complex.h

#ifndef COMPLEX_H
#define COMPLEX_H

class Complex {
private:
	double real;
	double imag;

public:
	Complex(double real = 0.0, double imag = 0.0);
	double getReal() const;
	void setReal(double real);
	double getImag() const;
	void setImag(double imag);
	void setValue(double real, double imag);
	void print() const;
	bool isReal() const;
	bool isImaginary() const;

	Complex &addInto(const Complex &another);
	Complex &addInto(double real, double imag);

	Complex addReturnNew(const Complex *another) const;
	Complex addReturnNew(double real, double imag) const;
};
#endif

Complex.cpp

#include <iostream>
#include "Complex.h"

using namespace std;

Complex::Complex(double real, double imag) : real(real), imag(imag) {}

double Complex::getReal() const {
	return real;
}

void Complex::setReal(double real) {
	this->real = real;
}

double Complex::getImag() const {
	return imag;
}

void Complex::setImag(double imag) {
	this->imag = imag;
}

void Complex::setValue(double real, double imag) {
	this->real = real;
	this->imag = imag;
}

void Complex::print() const {
	cout << '(' << real << ',' << imag << ')' << endl;
}

bool Complex::isReal() const {
	return (imag == 0);
}

bool Complex::isImaginary() const {
	return (imag != 0);
}

Complex &Complex::addInto(const Complex &another) {
	real += another.real;
	imag += another.imag;
	return *this;
}

Complex &Complex::addInto(double real, double imag) {
	this->real += real;
	this->imag += imag;
	return *this;
}

Complex Complex::addReturnNew(const Complex &another) const {
	return Complex(real + another.real, imag + another.imag);
}

Complex Complex::addReturnNew(double real, double imag) const {
	return Complex(this->real + real, this->imag + imag);
}

TestComplex.cpp

#include <iostream>
#include <iomanip>
#include "Complex.h"

using namespace std;

int main() {
	Complex c1, c2(4, 5);
	c1.print();
	c2.print();

	c1.setValue(6, 7);
	c1.print();

	c1.setReal(0);
	c1.setImag(8);
	c1.print();

	cout << boolalpha; // print true/false instead of 0/1
	cout << "Is real?" << c1.isReal() << endl;
	cout << "Is Imaginary?" << c1.isImaginary() << endl;

	c1.addInto(c2).addInto(1, 1).print();
	c1.print();

	c1.addReturnNew(c2).print();
	c1.print();
	c1.addReturnNew(1, 1).print();
	c1.print();

	return 0;
}

**注意:**不要返回一个局部变量的引用!

假设我们将addReturnNew函数修改为下面的样子:

Complex &Complex::addReturnNew(const Complex &another) const {
	return Complex(real + another.real, imag + another.imag);
}

那么在编译的时候会报以下错误:“invalid initialization of non-const reference of type ‘Complex&’ from an rvalue of type ‘Complex’"。 这是因为临时变量是在函数体内构造的,不能作用于函数体外,从而外部调用的引用就是非法的。

5. 示例:“Date"类

Date.h

#ifndef DATE_H
#define DATE_H

#include <string>

using namespace std;

class Date {
private:
	int year;
	int month;
	int day;
	const static string STR_MONTHS[];
	const static string STR_DAYS[];
	const static int DAYS_IN_MONTHS[];
	const static int YRER_MIN = 1753;
	const static int YRER_MAX = 9999;

public:
	static bool isLeapYear(int y);
	static bool isValidDate(int y, int m, int d);
	static int getDayOfWeek(int y, int m, int d);

	Date(int y, int m, int d);
	void setDate(int y, int m, int d);
	int getYear() const;
	int getMonth() const;
	int getDay() const;
	void setYear(int y);
	void setMonth(int m);
	void setDay(int d);
	void print() const;

	Date &nextDay();
	Date &previousDay();
	Date &nextMonth();
	Date &previousMonth();
	Date &nextYear();
	Date &previousYear();
};
#endif

Date.cpp

/* Implementation for Date Class (Date.cpp) */
#include <iostream>
#include <stdexcept>
#include "Date.h"
using namespace std;

// Initialize static non-integer variable (must be done outside the class declaration)
const string Date::STR_MONTHS[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
                                   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

const int Date::DAYS_IN_MONTHS[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

const string Date::STR_DAYS[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
                                 "Thursday", "Friday", "Saturday"};

// A static function that returns true if the given year is a leap year
bool Date::isLeapYear(int year) {
   return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
}

// A static function that returns true if the given y, m, d constitutes a valid date
bool Date::isValidDate(int y, int m, int d) {
   if (y >= YEAR_MIN && y <= YEAR_MAX && m >= 1 && m <= 12) {
      int lastDayOfMonth = DAYS_IN_MONTHS[m-1];
      if (m == 2 && isLeapYear(y)) {
         lastDayOfMonth = 29;
      }
      return (d >= 1 && d <= lastDayOfMonth);
   } else {
      return false;
   }
}

// A static function that returns the day of the week (0:Sun, 6:Sat) for the given date
// Wiki "Determination of the day of the week" for the algorithm
int Date::getDayOfWeek(int y, int m, int d) {
   int centuryTable[] = {4, 2, 0, 6, 4, 2, 0, 6}; // 17xx, 18xx, ...
   int MonthTable[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
   int MonthLeapYearTable[] = {6, 2, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};

   int century = y / 100;
   int twoDigitYear = y % 100;
   int centuryTableIndex = (century - 17) % 8;
   // Date before 17xx are not valid, but needed to prevent negative index
   if (centuryTableIndex < 0) {
      centuryTableIndex += 8;
   }
   int sum = centuryTable[centuryTableIndex] + twoDigitYear + twoDigitYear / 4;
   if (isLeapYear(y)) {
      sum += MonthLeapYearTable[m-1];
   } else {
      sum += MonthTable[m-1];
   }
   sum += d;
   return sum % 7;
}

// Constructor
Date::Date(int y, int m, int d) {
   setDate(y, m, d);
}

// With Input validation
void Date::setDate(int y, int m, int d) {
   setYear(y);
   setMonth(m);
   setDay(d); // need to set the day after year and month
}

int Date::getYear() const {
   return year;
}

void Date::setYear(int y) {
   if (y >= YEAR_MIN && y <= YEAR_MAX) {
      year = y;
   } else {
      throw invalid_argument("Error: Invalid year (1753-9999)!");
   }
}

int Date::getMonth() const {
   return month;
}

void Date::setMonth(int m) {
   if (m >= 1 && m <= 12) {
      month = m;
   } else {
      throw invalid_argument("Error: Invalid month (1-12)!");
   }
}

int Date::getDay() const {
   return day;
}

// Assuming that the year and month are already set
void Date::setDay(int d) {
   int lastDayOfMonth = DAYS_IN_MONTHS[month-1];
   if (month == 2 && isLeapYear(year)) {
      lastDayOfMonth = 29;
   }
   if (d >= 1 && d <= lastDayOfMonth) {
      day = d;
   } else {
      throw invalid_argument("Error: Invalid day (1-28|29|30|31)!");
   }
}

// Print this instance in the format "xxxday, d mmm yyyy".
void Date::print() const {
   cout << STR_DAYS[getDayOfWeek(year, month, day)] << ", "
        << day << " " << STR_MONTHS[month-1] << " " << year << endl;
}

// Increment this instance to the next day and return this instance by reference
Date& Date::nextDay() {
   int lastDayOfMonth = DAYS_IN_MONTHS[month-1];
   if (month == 2 && isLeapYear(year)) {
      lastDayOfMonth = 29;
   }

   // check day against the end of month
   if (++day > lastDayOfMonth) {
      day = 1;
      if (++month > 12) {
         month = 1;
         if (++year > YEAR_MAX) {
            throw out_of_range("Error: Next day is out of range!");
         }
      }
   }
   return *this;
}

// Decrement this instance to the previous day and return this instance by reference
Date& Date::previousDay() {
   int lastDayOfMonth = DAYS_IN_MONTHS[month-1];
   if (month == 2 && isLeapYear(year)) {
      lastDayOfMonth = 29;
   }

   // check day against the end of month
   if (--day < 1) {
      day = lastDayOfMonth;
      if (--month < 1) {
         month = 12;
         if (--year < YEAR_MIN) {
            throw out_of_range("Error: Previous day is out of range!");
         }
      }
   }
   return *this;
}

// Increment this instance to the next month and return this instance by reference
Date& Date::nextMonth() {
   if (++month > 12) {
      month = 1;
      if (++year > YEAR_MAX) {
         throw out_of_range("Error: Next month is out of range!");
      }
   }
   // may need to adjust the last day of the month
   int lastDayOfMonth = DAYS_IN_MONTHS[month-1];
   if (month == 2 && isLeapYear(year)) {
      lastDayOfMonth = 29;
   }
   if (day > lastDayOfMonth) {
      day = lastDayOfMonth;
   }
   return *this;
}

// Decrement this instance to the previous month and return this instance by reference
Date& Date::previousMonth() {
   if (--month < 1) {
      month = 12;
      if (--year < YEAR_MIN) {
         throw out_of_range("Error: Previous month is out of range!");
      }
   }
   // may need to adjust the last day of the month
   int lastDayOfMonth = DAYS_IN_MONTHS[month-1];
   if (month == 2 && isLeapYear(year)) {
      lastDayOfMonth = 29;
   }
   if (day > lastDayOfMonth) {
      day = lastDayOfMonth;
   }
   return *this;
}

// Increment this instance to the next year and return this instance by reference
Date& Date::nextYear() {
   if (++year > YEAR_MAX) {
      throw out_of_range("Error: Next year is out of range!");
   }
   // may need to adjust the last day of the month for leap year (29 Feb)
   //  to non-leap year (28 Feb)
   if (month == 2 && day == 29 && !isLeapYear(year)) {
      day = 28;
   }
   return *this;
}

// Decrement this instance to the previous year and return this instance by reference
Date& Date::previousYear() {
   if (--year < YEAR_MIN) {
      throw out_of_range("Error: Previous year is out of range!");
   }
   // may need to adjust the last day of the month for leap year (29 Feb)
   //  to non-leap year (28 Feb)
   if (month == 2 && day == 29 && !isLeapYear(year)) {
      day = 28;
   }
   return *this;
}

TestDate.cpp

/* Test Driver Program (TestDate.cpp) */
#include <iostream>
#include <stdexcept>
#include "Date.h"

int main() {
   Date d1(2012, 1, 1);
   d1.print();  // Sunday, 1 Jan 2012
   d1.nextDay().print();  // Monday, 2 Jan 2012
   d1.print();  // Monday, 2 Jan 2012

   d1.setDate(2012, 1, 31);
   d1.print();  // Tuesday, 31 Jan 2012
   d1.nextDay().print();  // Wednesday, 1 Feb 2012

   d1.setDate(2012, 2, 28);
   d1.print();  // Tuesday, 28 Feb 2012
   d1.nextDay().print();  // Wednesday, 29 Feb 2012

   d1.setDate(2012, 12, 31);
   d1.print();  // Monday, 31 Dec 2012
   d1.nextDay().print();  // Tuesday, 1 Jan 2013

//   Date d2(2011, 2, 29);  // abrupt termination!
//   d2.print();

   try {  // graceful handling of exception
      Date d3(2011, 2, 29);
      d3.print();
   } catch (invalid_argument &ex) {
      cout << ex.what() << endl;  // Error: Invalid day (1-28|29|30|31)!
   }
   cout << "Next Statement after try-catch" << endl;

   try {  // graceful handling of exception
      Date d4(9999, 12, 30);
      d4.nextDay().print(); // Friday, 31 Dec 9999
      d4.nextDay();
      d4.print();
   } catch (out_of_range &ex) {
      cout << ex.what() << endl;  // Error: Next day is outside the valid range!
   }

   Date d5(2012, 1, 1);
   d5.previousDay().print();  // Saturday, 31 Dec 2011

   Date d6(2012, 3, 31);
   d6.nextMonth().print();  // Monday, 30 Apr 2012

   Date d7(2012, 3, 31);
   d7.previousMonth().print();  // Wednesday, 29 Feb 2012

   Date d8(2012, 2, 29);
   d8.nextYear().print(); // Thursday, 28 Feb 2013

   Date d9(2012, 2, 29);
   d9.previousYear().print();  // Monday, 28 Feb 2011
}