C++入门(类和对象之实现日期类)
文章目录
日期类的实现
1、构造函数
//在类里声明,类外实现
Date::Date(int year, int month, int day)
{if (year >= 1 &&month <= 12 && month >= 1 &&day >= 1 && day <= GetMonthDay(year, month)){_year = year;_month = month;_day = day;}else{cout << "非法日期" << endl;}
}
2、拷贝构造
//代码小于5行,直接在类里面实现
Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}
3、判断天数函数
int Date::GetMonthDay(int year, int month)
{assert(year >= 0 && month > 0 && month < 13);//断言const static int monthDayArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };//用static修饰数组,将数组放在静态区,不用每次执行判断函数都要开辟这块数组空间,提高效率//再加一个const,让别人不能修改这个数组里的数据if (month == 2 && isLeapYear(year)){return 29;}else{return monthDayArray[month];}
}
4、运算符重载
4.1、加法
// d1 + 100 --> d1.operator+(day);
Date Date::operator+(int day)
{Date ret(*this);//拷贝构造一个和d1一样的对象,这样就不会改变d1的值ret._day += day;while (ret._day > GetMonthDay(ret._year, ret._month))//天数超过该月天数就进1{ret._day -= GetMonthDay(ret._year, ret._month);//减去该月天数ret._month++;if (ret._month == 13)//如果月超过12,年就进1{++ret._year;ret._month = 1;}}return ret;//返回我们拷贝构造之后的对象
}
4.2、赋值运算符重载
Date& operator=(const Date& d){if (this != &d)//判断是不是自己给自己赋值{_year = d._year;_month = d._month;_day = d._day;}return *this;}
4.3、加等
// d1 += 100
Date& Date::operator+=(int day)
{if (day < 0)//如果加的数是负数return *this -= -day;//等于调用减等函数_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){++_year;_month = 1;}}return *this;//由于出了加等函数的作用域,*this还没有被销毁,所以我们可以用传引用返回
}
由于加法和加等的逻辑相同,所以我们可以复用加等的代码来实现加法
Date Date::operator+(int day)
{Date ret(*this);ret += day;return ret;
}
4.4、减等、减法
// d1 -= 100
Date& Date::operator-=(int day)
{if (day < 0)//如果减等数是负数return *this += -day;//等于调用加等函数_day -= day;while (_day <= 0){--_month;if (_month == 0){_month = 12;--_year;}_day += GetMonthDay(_year, _month);}return *this;
}
由于减等和减法的逻辑相同,所以我们可以复用减等的代码来实现减法
Date Date::operator-(int day)
{Date ret = *this;ret -= day;return ret;
}
4.5、前置++和后置++
- 由于前置++和后置++函数都是operator++()
- 所以C++规定,利用函数重载规则,不加参数的operator++()是前置++
- 加参数的operator++(int i)是后置++
//前置++代码
// ++d1Date& operator++() // 前置{*this += 1;return *this;//出了作用域*this还在,所以可以用传引用返回}
后置++
//后置++代码
// d1++Date operator++(int) //这里不具体接收参数的原因:传参只是为了让编译器判断是不是后置++{Date tmp(*this);*this += 1;return tmp;//出了作用域tmp就被销毁了,所以只能用传值返回,传值返回就需要拷贝}
4.6、小于和小于等于
//小于运算符重载
bool Date::operator<(const Date& d)
{if ((_year < d._year)|| (_year == d._year && _month < d._month)|| (_year == d._year && _month == d._month && d._day < d._day)){return true;}else{return false;}
}
等于运算符重载
// d1 == d2
bool Date::operator==(const Date& d)
{return _year == d._year&& _month == d._month&& _day == d._day;
}
- 实现了小于和等于函数之后,其他的比较大小的运算符都可以用小于函数和等于函数进行复用
- 由于代码复用,所以其他函数的代码行数都比较少,可以直接写在类里面,变成内联(inline)函数
// inline不支持声明和定义分别放到.h 和.cpp// 所以成员函数中要成为inline最好直接在类里面定义// 类里面定义默认就是inlinebool operator>(const Date& d){return !(*this <= d);}bool operator>=(const Date& d){return !(*this < d);}bool operator!=(const Date& d){return !(*this == d);}// d1 <= d2bool operator<=(const Date& d){return *this < d || *this == d;}
4.7、日期 减去 日期函数
// d1 - d2
int Date::operator-(const Date& d)
{int flag = 1;Date max = *this;Date min = d;if (*this < d){min = *this;max = d;flag = -1;}int n = 0;while (min != max){++n;++min;}return n * flag;
}
4.8、流插入<< 和 流提取>> 运算符重载
- 要想进行流提取和流插入的运算符重载,首先我们就要知道首先,cout是iostream中定义的ostream类的对象
- <<能用在cout上是因为,在ostream类里对<<进行了函数重载,把内置类型都重载了一遍
- cout << 1相当于执行函数cout.operator<<(1)
cout << "this"相当于执行函数cout.operator<<(“this”) - 如果在程序中希望对<<运算符进行重载,使其能够输出自定义的数据,如何实现呢?
由于ostream类型已经在iostream中实现,所以不能作为ostream类的成员函数重载,只能作为全局函数或友元函数重载。 - 流插入重载
//流插入重载
std::ostream& operator<<(std::ostream& out, const Date& d)
{out << d._year << "_" << d._month << "_" << d.day << endl;return out;
}
- 流提取重载
std::istream& operator>>(std::istream& in, Date& d)
//流提取不加const的原因:流插入就是把从流里面提取到的值写入到d里
//如果用了const就只能读不能写
{in >> d._year >> d._month >> d.day;return in;
}
- 注意
- 写这两个重载函数的时候要在类里面写友元函数来声明,否则访问不到私有的成员变量
- 友元函数表示让编译器知道这个函数是类的朋友,可以访问类的私有成员变量
friend std::ostream& operator<<(std::ostream out,const Date& d);//流插入
friend std::istream& operator>>(std::istream out,Date& d);//流提取
小技巧
1、如何判断该传值返回还是传引用返回
- 自定义类型的传值返回需要调用拷贝构造,所以为了提升代码效率,我们有时使用传引用返回
- 判断返回的对象除了函数作用域之后还存不存在,如果存在,就用传引用返回,如果不存在,就用传值返回
- 如果实在判断不出来就使用传值返回,传值返回一定是对的,当不一定是高效的
2、const修饰成员函数
- 建议成员函数中不修改成员变量的成员函数,都可以加上const
- 这样普通对象和const对象都可以调用
- 如果声明和定义分离,声明和定义都要加const
3、关于const修饰成员函数的几个小问题
- const对象可以调用非const成员函数吗?(不能,这是权限放大)
- 非const对象可以调用const成员函数吗?(可以,这是权限缩小)
- const成员函数内可以调用其他非const成员函数吗?(不能,这是权限放大)
- 非const成员函数内可以调用其他const成员函数吗?(可以,这是权限缩小)
//void Print(const Date* const this)
void Print() const//相当于上面那行代码{cout << _year << "-" << _month << "-" << _day << endl;}
- 原则:成员函数只要不改成员变量,都建议加const,例子如下
bool opeartor>(const Date& d) const
{return true;
}