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
}
评论