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

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

Time.h

1class Time {
2private:
3	......
4public:
5	Time &nextSecond(); // Return a reference to "this" instance
6	......
7}

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

Time.cpp

 1// Increase this instance by one second and return this instance by reference.
 2Time &Time::nextSecond() {
 3	if (++second == 60) {
 4		second = 0;
 5		if (++minute == 60) {
 6			minute = 0;
 7			if (++hour = 24) {
 8				hour = 0;
 9			}
10		}
11	}
12
13	return *this; // Return this instance by reference
14				  // "this" is a pointer to this instance. *this refers to the instance.
15}

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

TestTime.cpp

1Time t1(23, 59, 58);
2t1.print();
3t2.nextSecond();
4t1.print();
5t1.nextSecond().nextSecond().print();
6t1.print();

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

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

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

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

Time.h

 1#ifndef TIME_H
 2#define TIME_H
 3
 4class Time {
 5private:
 6	int hour;
 7	int minute;
 8	int second;
 9
10public:
11	Time(int h = 0, int m = 0, int s = 0);
12	int getHour() const;
13	void setHour(int h);
14	int getMinute() const;
15	void setMinute(int m);
16	int getSecond() const;
17	void setSecond(int s);
18	void setTime(int h, int m, int s);
19	void print() const;
20};
21
22#endif

Time.cpp

 1#include <iostream>
 2#include <iomanip>
 3#include <stdexcept>
 4#include "Time.h"
 5
 6using namespace std;
 7
 8Time::Time(int h, int m, int s) {
 9	setHour(h);
10	setMinute(m);
11	setSecond(s);
12}
13
14int Time::getHour() const {
15	return hour;
16}
17
18void Time::setHour(int h) {
19	if (h >= 0 && h <= 23) {
20		hour = h;
21	} else {
22		throw invalid_argument("Invalid hour! Hour shall be 0~23.");
23	}
24}
25
26int Time::getMinute() const {
27	return minute;
28}
29
30void Time::setMinute(int m) {
31	if (m >= 0 && m <= 59) {
32		minute = m;
33	} else {
34		throw invalid_argument("Invalid minute! Minute shall be 0~59.");
35	}
36}
37
38int Time::getSecond() const {
39	return second;
40}
41
42void Time::setSecond(int s) {
43	if (s >= 0 && s <= 59) {
44		second = s;
45	} else {
46		throw invalid_argument("Invalid second! Second shall be 0~59.");
47	}
48}
49
50void Time::setTime(int h, int m, int s) {
51	setHour(h);
52	setMinute(m);
53	setSecond(s);
54}
55
56void Time::print() const {
57	cout << setfill('0');
58	cout << setw(2) << hour << ":" << setw(2) << minute << ":"
59		<< setw(2) << second << endl;
60}

TestTime.cpp

 1#include <iostream>
 2#include <stdexcept>
 3
 4#include "Time.h"
 5
 6using namespace std;
 7
 8int main() {
 9	try {
10		Time t1(25, 0, 0);
11		t1.print();
12	} catch (invalid_argument &ex) {
13		cout << "Exception:" << ex.what() << endl;
14	}
15
16	cout << "Next statement after try-catch" << endl;
17}

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

 1#include <iostream>
 2#include "Time.h"
 3using namespace tsd;
 4
 5int main() {
 6	Time t1(1, 2, 3);
 7	t1.print();
 8
 9	Time *ptrT1 = &t1;
10	(*ptrT1).print();
11	ptrT1->print();
12
13	Time &refT1 = t1;
14	refT1.print();
15
16	Time *ptrT2 = new Time(4, 5, 6);
17	ptrT2->print();
18	delete ptrT2;
19
20	Time tArray1[2];
21	tArray1[0].print();
22	tArray1[1].print();
23
24	Time tArray2[2] = {Time(7, 8, 9), Time(10)};
25	tArray2[0].print();
26	tArray2[1].print();
27
28	Time *ptrTarray3 = new Time[2];
29	ptrTarray3[0].print();
30	ptrTarray3[1].print();
31	delete[] ptrTarray3;
32
33	// C++11 syntax, compile with -std=c++0x
34	Time *ptrTarray4 = new Time[2] {Time(11, 12, 13), Time(14)};
35	ptrTarray4->print();
36	(ptrTarray4 + 1)->print();
37	delete[] ptrTarray4;
38}

4. 示例:复数类

下面是复数类的类图

Complex.h

 1#ifndef COMPLEX_H
 2#define COMPLEX_H
 3
 4class Complex {
 5private:
 6	double real;
 7	double imag;
 8
 9public:
10	Complex(double real = 0.0, double imag = 0.0);
11	double getReal() const;
12	void setReal(double real);
13	double getImag() const;
14	void setImag(double imag);
15	void setValue(double real, double imag);
16	void print() const;
17	bool isReal() const;
18	bool isImaginary() const;
19
20	Complex &addInto(const Complex &another);
21	Complex &addInto(double real, double imag);
22
23	Complex addReturnNew(const Complex *another) const;
24	Complex addReturnNew(double real, double imag) const;
25};
26#endif

Complex.cpp

 1#include <iostream>
 2#include "Complex.h"
 3
 4using namespace std;
 5
 6Complex::Complex(double real, double imag) : real(real), imag(imag) {}
 7
 8double Complex::getReal() const {
 9	return real;
10}
11
12void Complex::setReal(double real) {
13	this->real = real;
14}
15
16double Complex::getImag() const {
17	return imag;
18}
19
20void Complex::setImag(double imag) {
21	this->imag = imag;
22}
23
24void Complex::setValue(double real, double imag) {
25	this->real = real;
26	this->imag = imag;
27}
28
29void Complex::print() const {
30	cout << '(' << real << ',' << imag << ')' << endl;
31}
32
33bool Complex::isReal() const {
34	return (imag == 0);
35}
36
37bool Complex::isImaginary() const {
38	return (imag != 0);
39}
40
41Complex &Complex::addInto(const Complex &another) {
42	real += another.real;
43	imag += another.imag;
44	return *this;
45}
46
47Complex &Complex::addInto(double real, double imag) {
48	this->real += real;
49	this->imag += imag;
50	return *this;
51}
52
53Complex Complex::addReturnNew(const Complex &another) const {
54	return Complex(real + another.real, imag + another.imag);
55}
56
57Complex Complex::addReturnNew(double real, double imag) const {
58	return Complex(this->real + real, this->imag + imag);
59}

TestComplex.cpp

 1#include <iostream>
 2#include <iomanip>
 3#include "Complex.h"
 4
 5using namespace std;
 6
 7int main() {
 8	Complex c1, c2(4, 5);
 9	c1.print();
10	c2.print();
11
12	c1.setValue(6, 7);
13	c1.print();
14
15	c1.setReal(0);
16	c1.setImag(8);
17	c1.print();
18
19	cout << boolalpha; // print true/false instead of 0/1
20	cout << "Is real?" << c1.isReal() << endl;
21	cout << "Is Imaginary?" << c1.isImaginary() << endl;
22
23	c1.addInto(c2).addInto(1, 1).print();
24	c1.print();
25
26	c1.addReturnNew(c2).print();
27	c1.print();
28	c1.addReturnNew(1, 1).print();
29	c1.print();
30
31	return 0;
32}

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

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

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

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

5. 示例:Date"类

Date.h

 1#ifndef DATE_H
 2#define DATE_H
 3
 4#include <string>
 5
 6using namespace std;
 7
 8class Date {
 9private:
10	int year;
11	int month;
12	int day;
13	const static string STR_MONTHS[];
14	const static string STR_DAYS[];
15	const static int DAYS_IN_MONTHS[];
16	const static int YRER_MIN = 1753;
17	const static int YRER_MAX = 9999;
18
19public:
20	static bool isLeapYear(int y);
21	static bool isValidDate(int y, int m, int d);
22	static int getDayOfWeek(int y, int m, int d);
23
24	Date(int y, int m, int d);
25	void setDate(int y, int m, int d);
26	int getYear() const;
27	int getMonth() const;
28	int getDay() const;
29	void setYear(int y);
30	void setMonth(int m);
31	void setDay(int d);
32	void print() const;
33
34	Date &nextDay();
35	Date &previousDay();
36	Date &nextMonth();
37	Date &previousMonth();
38	Date &nextYear();
39	Date &previousYear();
40};
41#endif

Date.cpp

  1/* Implementation for Date Class (Date.cpp) */
  2#include <iostream>
  3#include <stdexcept>
  4#include "Date.h"
  5using namespace std;
  6
  7// Initialize static non-integer variable (must be done outside the class declaration)
  8const string Date::STR_MONTHS[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  9                                   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
 10
 11const int Date::DAYS_IN_MONTHS[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 12
 13const string Date::STR_DAYS[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
 14                                 "Thursday", "Friday", "Saturday"};
 15
 16// A static function that returns true if the given year is a leap year
 17bool Date::isLeapYear(int year) {
 18   return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
 19}
 20
 21// A static function that returns true if the given y, m, d constitutes a valid date
 22bool Date::isValidDate(int y, int m, int d) {
 23   if (y >= YEAR_MIN && y <= YEAR_MAX && m >= 1 && m <= 12) {
 24      int lastDayOfMonth = DAYS_IN_MONTHS[m-1];
 25      if (m == 2 && isLeapYear(y)) {
 26         lastDayOfMonth = 29;
 27      }
 28      return (d >= 1 && d <= lastDayOfMonth);
 29   } else {
 30      return false;
 31   }
 32}
 33
 34// A static function that returns the day of the week (0:Sun, 6:Sat) for the given date
 35// Wiki "Determination of the day of the week" for the algorithm
 36int Date::getDayOfWeek(int y, int m, int d) {
 37   int centuryTable[] = {4, 2, 0, 6, 4, 2, 0, 6}; // 17xx, 18xx, ...
 38   int MonthTable[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
 39   int MonthLeapYearTable[] = {6, 2, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
 40
 41   int century = y / 100;
 42   int twoDigitYear = y % 100;
 43   int centuryTableIndex = (century - 17) % 8;
 44   // Date before 17xx are not valid, but needed to prevent negative index
 45   if (centuryTableIndex < 0) {
 46      centuryTableIndex += 8;
 47   }
 48   int sum = centuryTable[centuryTableIndex] + twoDigitYear + twoDigitYear / 4;
 49   if (isLeapYear(y)) {
 50      sum += MonthLeapYearTable[m-1];
 51   } else {
 52      sum += MonthTable[m-1];
 53   }
 54   sum += d;
 55   return sum % 7;
 56}
 57
 58// Constructor
 59Date::Date(int y, int m, int d) {
 60   setDate(y, m, d);
 61}
 62
 63// With Input validation
 64void Date::setDate(int y, int m, int d) {
 65   setYear(y);
 66   setMonth(m);
 67   setDay(d); // need to set the day after year and month
 68}
 69
 70int Date::getYear() const {
 71   return year;
 72}
 73
 74void Date::setYear(int y) {
 75   if (y >= YEAR_MIN && y <= YEAR_MAX) {
 76      year = y;
 77   } else {
 78      throw invalid_argument("Error: Invalid year (1753-9999)!");
 79   }
 80}
 81
 82int Date::getMonth() const {
 83   return month;
 84}
 85
 86void Date::setMonth(int m) {
 87   if (m >= 1 && m <= 12) {
 88      month = m;
 89   } else {
 90      throw invalid_argument("Error: Invalid month (1-12)!");
 91   }
 92}
 93
 94int Date::getDay() const {
 95   return day;
 96}
 97
 98// Assuming that the year and month are already set
 99void Date::setDay(int d) {
100   int lastDayOfMonth = DAYS_IN_MONTHS[month-1];
101   if (month == 2 && isLeapYear(year)) {
102      lastDayOfMonth = 29;
103   }
104   if (d >= 1 && d <= lastDayOfMonth) {
105      day = d;
106   } else {
107      throw invalid_argument("Error: Invalid day (1-28|29|30|31)!");
108   }
109}
110
111// Print this instance in the format "xxxday, d mmm yyyy".
112void Date::print() const {
113   cout << STR_DAYS[getDayOfWeek(year, month, day)] << ", "
114        << day << " " << STR_MONTHS[month-1] << " " << year << endl;
115}
116
117// Increment this instance to the next day and return this instance by reference
118Date& Date::nextDay() {
119   int lastDayOfMonth = DAYS_IN_MONTHS[month-1];
120   if (month == 2 && isLeapYear(year)) {
121      lastDayOfMonth = 29;
122   }
123
124   // check day against the end of month
125   if (++day > lastDayOfMonth) {
126      day = 1;
127      if (++month > 12) {
128         month = 1;
129         if (++year > YEAR_MAX) {
130            throw out_of_range("Error: Next day is out of range!");
131         }
132      }
133   }
134   return *this;
135}
136
137// Decrement this instance to the previous day and return this instance by reference
138Date& Date::previousDay() {
139   int lastDayOfMonth = DAYS_IN_MONTHS[month-1];
140   if (month == 2 && isLeapYear(year)) {
141      lastDayOfMonth = 29;
142   }
143
144   // check day against the end of month
145   if (--day < 1) {
146      day = lastDayOfMonth;
147      if (--month < 1) {
148         month = 12;
149         if (--year < YEAR_MIN) {
150            throw out_of_range("Error: Previous day is out of range!");
151         }
152      }
153   }
154   return *this;
155}
156
157// Increment this instance to the next month and return this instance by reference
158Date& Date::nextMonth() {
159   if (++month > 12) {
160      month = 1;
161      if (++year > YEAR_MAX) {
162         throw out_of_range("Error: Next month is out of range!");
163      }
164   }
165   // may need to adjust the last day of the month
166   int lastDayOfMonth = DAYS_IN_MONTHS[month-1];
167   if (month == 2 && isLeapYear(year)) {
168      lastDayOfMonth = 29;
169   }
170   if (day > lastDayOfMonth) {
171      day = lastDayOfMonth;
172   }
173   return *this;
174}
175
176// Decrement this instance to the previous month and return this instance by reference
177Date& Date::previousMonth() {
178   if (--month < 1) {
179      month = 12;
180      if (--year < YEAR_MIN) {
181         throw out_of_range("Error: Previous month is out of range!");
182      }
183   }
184   // may need to adjust the last day of the month
185   int lastDayOfMonth = DAYS_IN_MONTHS[month-1];
186   if (month == 2 && isLeapYear(year)) {
187      lastDayOfMonth = 29;
188   }
189   if (day > lastDayOfMonth) {
190      day = lastDayOfMonth;
191   }
192   return *this;
193}
194
195// Increment this instance to the next year and return this instance by reference
196Date& Date::nextYear() {
197   if (++year > YEAR_MAX) {
198      throw out_of_range("Error: Next year is out of range!");
199   }
200   // may need to adjust the last day of the month for leap year (29 Feb)
201   //  to non-leap year (28 Feb)
202   if (month == 2 && day == 29 && !isLeapYear(year)) {
203      day = 28;
204   }
205   return *this;
206}
207
208// Decrement this instance to the previous year and return this instance by reference
209Date& Date::previousYear() {
210   if (--year < YEAR_MIN) {
211      throw out_of_range("Error: Previous year is out of range!");
212   }
213   // may need to adjust the last day of the month for leap year (29 Feb)
214   //  to non-leap year (28 Feb)
215   if (month == 2 && day == 29 && !isLeapYear(year)) {
216      day = 28;
217   }
218   return *this;
219}

TestDate.cpp

 1/* Test Driver Program (TestDate.cpp) */
 2#include <iostream>
 3#include <stdexcept>
 4#include "Date.h"
 5
 6int main() {
 7   Date d1(2012, 1, 1);
 8   d1.print();  // Sunday, 1 Jan 2012
 9   d1.nextDay().print();  // Monday, 2 Jan 2012
10   d1.print();  // Monday, 2 Jan 2012
11
12   d1.setDate(2012, 1, 31);
13   d1.print();  // Tuesday, 31 Jan 2012
14   d1.nextDay().print();  // Wednesday, 1 Feb 2012
15
16   d1.setDate(2012, 2, 28);
17   d1.print();  // Tuesday, 28 Feb 2012
18   d1.nextDay().print();  // Wednesday, 29 Feb 2012
19
20   d1.setDate(2012, 12, 31);
21   d1.print();  // Monday, 31 Dec 2012
22   d1.nextDay().print();  // Tuesday, 1 Jan 2013
23
24//   Date d2(2011, 2, 29);  // abrupt termination!
25//   d2.print();
26
27   try {  // graceful handling of exception
28      Date d3(2011, 2, 29);
29      d3.print();
30   } catch (invalid_argument &ex) {
31      cout << ex.what() << endl;  // Error: Invalid day (1-28|29|30|31)!
32   }
33   cout << "Next Statement after try-catch" << endl;
34
35   try {  // graceful handling of exception
36      Date d4(9999, 12, 30);
37      d4.nextDay().print(); // Friday, 31 Dec 9999
38      d4.nextDay();
39      d4.print();
40   } catch (out_of_range &ex) {
41      cout << ex.what() << endl;  // Error: Next day is outside the valid range!
42   }
43
44   Date d5(2012, 1, 1);
45   d5.previousDay().print();  // Saturday, 31 Dec 2011
46
47   Date d6(2012, 3, 31);
48   d6.nextMonth().print();  // Monday, 30 Apr 2012
49
50   Date d7(2012, 3, 31);
51   d7.previousMonth().print();  // Wednesday, 29 Feb 2012
52
53   Date d8(2012, 2, 29);
54   d8.nextYear().print(); // Thursday, 28 Feb 2013
55
56   Date d9(2012, 2, 29);
57   d9.previousYear().print();  // Monday, 28 Feb 2011
58}