2017年11月11日
《その116》 復習用プログラム(p.26演習1-2)
前回《その115》の復習用プログラムを、改変してまとめるのが、p.26 演習1-2 です。
練習になるので、やや大幅に改変しました。
一番の変更点は、プログラムの複数箇所に散らばっていた日付調整用の作業を、新しく加えた日付調整用のメンバ関数 adjust が一手に引き受けるようにしたことです。
また、このメンバ関数 adjust は、例えば、2000/2/30, 1990/500/100 のような不適切な日付表現を適切に調整します。
ただし、調整後の日付が 1970/01/01 より前になってしまう場合にはプログラムが終了します。
それと、せっかく Date型クラスオブジェクトを扱う抽出子が定義されているので、日付の入力に使用するようにしました。
新版明解C++中級編 p.26 演習1-2
前回《その115》で学習したクラス Date は、たとえば、2015年2月30日といった不正な日付での初期化や代入などを許してしまう。コンストラクタなどに与えられた日付が不正であれば、正しい日付に補正するように書きかえたクラスを作成せよ。
// Date.h
#ifndef ___ClassDate
#define ___ClassDate
#include <string>
#include <iostream>
class Date {
int y; //【01】年
int m; // 月
int d; // 日
//【02】各月の日数(静的データメンバ)
static int days_in_month[];
//【03】うるう年であるか(静的メンバ関数)
static bool leap_year(int year);
public:
//【04】デフォルトコンストラクタ
Date();
//【05】コンストラクタ(年月日指定)
explicit Date(int yy, int mm = 1, int dd = 1);
//【34】年月日調整
void adjust();
//【06】曜日(0〜6)を求める
int day_of_week() const;
//【07】変換関数(1970/1/1からの日数)
operator long() const;
Date& operator+=(int dn); //【08】dn日進める
Date& operator-=(int dn); //【09】dn日戻す
//【10】日付の日数差
long operator-(const Date& day) const;
//【11】出力用の文字列作成
std::string to_string() const;
int& y_() { return y; } //【14】年にアクセス
int& m_() { return m; } //【15】月にアクセス
int& d_() { return d; } //【16】日にアクセス
//【17】年月日のセッタ
void set_ymd(int y, int m, int d);
Date d_before() const; //【18】前日の日付
Date next_day() const; //【19】翌日の日付
int ds_elpsd_y() const; //【20】年内経過日数
Date& operator++(); //【21】前置増分演算子
Date operator++(int); //【22】後置増分演算子
Date& operator--(); //【23】前置減分演算子
Date operator--(int); //【24】後置減分演算子
//【25-1】dn日後の日付
Date operator+(int dn) const;
//【25-2】dn日後の日付
friend Date operator+(int dn, const Date& x);
//【26】dn日前の日付
Date operator-(int dn) const;
//【27】operator==
bool operator== (const Date& x) const;
//【28】operator!=
bool operator!= (const Date& x) const;
//【29】operator<
bool operator< (const Date& x) const;
//【30】operator<=
bool operator<= (const Date& x) const;
//【31】operator>
bool operator> (const Date& x) const;
//【32】operator>=
bool operator>= (const Date& x) const;
//【33】うるう年であるか
bool leap_year() const;
};
//【12】挿入個の多重定義
std::ostream& operator<<(std::ostream& s, const Date& x);
//【13】抽出子の多重定義
std::istream& operator>>(std::istream& s, Date& x);
#endif
// Date.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <ctime>
#include <sstream>
#include <iomanip>
#include "Date.h"
using namespace std;
//【02】各月の日数
int Date::days_in_month[]
= { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
//【03】うるう年であるか
bool Date::leap_year(int year) {
return year % 4 == 0 && year % 100 != 0 ||
year % 400 == 0;
}
//【04】デフォルトコンストラクタ(今日の日付)
Date::Date() {
time_t current = time(NULL);
struct tm* local = localtime(&current);
y = local->tm_year + 1900;
m = local->tm_mon + 1;
d = local->tm_mday;
adjust(); // adjust関数で年月日調整
}
//【05】コンストラクタ(年月日指定)
Date::Date(int yy, int mm, int dd)
: y(yy), m(mm), d(dd) {
adjust(); // adjust関数で年月日調整
}
//【34】年月日調整
// 13月は翌年 1月, 14月は翌年 2月, ・・・と解釈
// d < 1 はエラー
// m < 1 はエラー
// y < 1970 はエラー
void Date::adjust() {
if (y < 1970)
throw out_of_range("error! : 1970/01/01より前の日付は不正です。");
if (m < 1)
throw out_of_range("error! : 月の値が1未満は不正です。");
if (d < 1)
throw out_of_range("error! : 日付が1未満は不正です。");
while (m > 12) {
m -= 12; y++;
}
while (d >(days_in_month[m - 1] + (leap_year(y) && m == 2))) {
// days_in_month[m - 1] + (leap_year(y) は該当月の日数
d -= (days_in_month[m - 1] + (leap_year(y) && m == 2));
if (++m > 12) {
y++; m = 1;
}
}
}
//【06】曜日(0〜6)を求める
int Date::day_of_week() const {
int yy = y; int mm = m;
if (mm == 1 || mm == 2) { yy--; mm += 12; }
return (yy + yy / 4 - yy / 100 + yy
/ 400 + (13 * mm + 8) / 5 + d) % 7;
}
//【07】変換関数(1970/1/1からの日数)
Date::operator long() const {
long dys = 0;
for (int i = 1970; i < y; i++)
dys += (365 + leap_year(i));
for (int i = 1; i < m; i++) {
dys += days_in_month[i - 1];
}
if (m > 2)
dys += leap_year(y);
return dys - 1 + d;
}
//【08】dn日進める
Date& Date::operator+=(int dn) {
if (dn < 0)
return *this -= -dn;
d += dn;
int temp_ds = days_in_month[m - 1]
+ (m == 2 && leap_year(y));
while (d > temp_ds) {
d -= temp_ds;
if (++m > 12) {
y++; m = 1;
}
temp_ds = days_in_month[m - 1]
+ (m == 2 && leap_year(y));
}
return *this;
}
//【09】dn日戻す
Date& Date::operator-=(int dn) {
if (dn < 0)
return *this += -dn;
d -= dn;
while (d < 1) {
if (--m < 1) {
y--; m = 12;
}
d += days_in_month[m - 1]
+ (m == 2 && leap_year(y));
}
adjust(); // adjust関数で年月日調整
return *this;
}
//【10】日付の日数差
long Date::operator-(const Date& day) const {
return long(*this) - long(day);
}
//【11】出力用の文字列作成
string Date::to_string() const {
string wd[]
= { "日", "月", "火", "水", "木", "金", "土", };
ostringstream s;
s << y << "年" << setw(2) << m << "月" << setw(2)
<< d << "日" << "(" << wd[day_of_week()] << ")";
return s.str();
}
//【17】年月日のセッタ
void Date::set_ymd(int y, int m, int d) {
this->y = y;
this->m = m;
this->d = d;
adjust(); // adjust関数で年月日調整
}
//【18】前日の日付
Date Date::d_before() const {
Date temp = *this;
return temp -= 1;
}
//【19】翌日の日付
Date Date::next_day() const {
Date temp = *this;
return temp += 1;
}
//【20】年内経過日数
int Date::ds_elpsd_y() const {
return *this - Date(y) + 1;
}
//【21】前置増分演算子
Date& Date::operator++() { return *this += 1; }
//【22】後置増分演算子
Date Date::operator++(int) {
Date temp(*this);
++(*this);
return temp;
}
//【23】前置減分演算子
Date& Date::operator--() { return *this -= 1; }
//【24】後置減分演算子
Date Date::operator--(int) {
Date temp(*this);
--(*this);
return temp;
}
//【25-1】dn日後の日付
Date Date::operator+(int dn) const {
Date temp(*this);
return temp += dn;
}
//【25-2】dn日後の日付
Date operator+(int dn, const Date& x) {
return x + dn;
}
//【26】dn日前の日付
Date Date::operator-(int dn) const {
Date temp(*this);
return temp -= dn;
}
//【27】operator==
bool Date::operator== (const Date& x) const {
return y == x.y && m == x.m && d == x.d;
}
//【28】operator!=
bool Date::operator!= (const Date& x) const {
return !(*this == x);
}
//【29】operator<
bool Date::operator< (const Date& x) const {
return long(*this) < long(x);
}
//【30】operator<=
bool Date::operator<= (const Date& x) const {
return long(*this) <= long(x);
}
//【31】operator>
bool Date::operator> (const Date& x) const {
return long(*this) > long(x);
}
//【32】operator>=
bool Date::operator>= (const Date& x) const {
return long(*this) >= long(x);
}
//【33】うるう年であるか
bool Date::leap_year() const { return leap_year(y); }
//【12】挿入子の多重定義
ostream& operator<<(ostream& s, const Date& x) {
return s << x.to_string();
}
//【13】抽出子の多重定義
istream& operator>>(istream& s, Date& x) {
int yy, mm, dd; char c;
s >> yy >> c >> mm >> c >> dd;
x.set_ymd(yy, mm, dd);
return s;
}
// DateTest.cpp
#include <string>
#include <iostream>
#include "Date.h"
using namespace std;
void input(Date& day) {
cout << "・日付を入力(**/**/**形式) : ";
cin >> day;
}
void display(const Date& day) {
cout << " " << day << " : ";
cout << "年内経過日数" << day.ds_elpsd_y() << "日";
cout << " 1970年1月1日の" << long(day) << "日後";
cout << " " << (day.leap_year() ? "うるう年" : "平年") << '\n';
}
void change(Date& day) {
while (true) {
cout << "◇[1]日付変更 "
<< "[2]n日進める [3]n日戻す [0]戻る:";
int selected;
cin >> selected;
if (!selected) return;
switch (selected) {
int n;
case 1: input(day); break;
case 2: cout << "・日数:"; cin >> n;
day += n;
break;
case 3: cout << "・日数:"; cin >> n;
day -= n;
break;
}
cout << " " << day << "に更新されました。\n";
}
}
void inc_dec(Date& day) {
while (true) {
cout << "◇[1]++day [2]day++ [3]--day "
"[4]day-- [0]戻る:";
int selected;
cin >> selected;
if (!selected) return;
switch (selected) {
case 1: cout << " ++day = " << ++day << '\n'; break;
case 2: cout << " day++ = " << day++ << '\n'; break;
case 3: cout << " --day = " << --day << '\n';
break;
case 4: cout << " day-- = " << day-- << '\n'; break;
break;
}
cout << " day = " << day << '\n';
}
}
void before_after(Date& day) {
while (true) {
cout << "◇[1]翌日 [2]前日 [3]n日後(day+n) "
<< "[4]n日後(n+day) [5]n日前 [0]戻る:";
int selected;
cin >> selected;
if (!selected) return;
int n;
if (selected >= 3 && selected <= 5) {
cout << "・日数:"; cin >> n;
}
cout << " ";
switch (selected) {
case 1: cout << day + 1; break;
case 2: cout << day - 1; break;
break;
case 3: cout << day + n; break;
case 4: cout << n + day; break;
case 5: cout << day - n; break;
break;
}
cout << '\n';
}
}
void compare(const Date& day) {
Date day2;
input(day2);
cout << boolalpha;
cout << "day = " << day << '\n';
cout << "day2 = " << day2 << '\n';
cout << "day - day2 = " << (day - day2) << '\n';
cout << "day2 - day = " << (day2 - day) << '\n';
cout << "day == day2 = " << (day == day2) << '\n';
cout << "day != day2 = " << (day != day2) << '\n';
cout << "day > day2 = " << (day > day2) << '\n';
cout << "day >= day2 = " << (day >= day2) << '\n';
cout << "day < day2 = " << (day < day2) << '\n';
cout << "day <= day2 = " << (day <= day2) << '\n';
}
int main() try {
Date day;
input(day); cout << '\n';
while (true) {
cout << "◆[1]日付情報 [2]日付変更 [3]増減分演算子 "
<< "[4]前後の日付 [5]比較 [0]終了:";
int selected;
cin >> selected;
if (!selected) break;
switch (selected) {
case 1: display(day); // 日付情報
cout << '\n'; break;
case 2: change(day); // 日付変更(更新)
cout << '\n'; break;
case 3: inc_dec(day); // ++演算子, --演算子
cout << '\n'; break;
case 4: before_after(day); // 日付
cout << '\n'; break;
case 5: compare(day); // 日付と日付の比較
cout << '\n'; break;
}
}
}
catch (out_of_range& e) {
cout << e.what() << '\n';
exit(1);
}
catch (...) {
cout << "不明なエラーで!!\n";
abort();
}
この記事へのコメント
コメントを書く
この記事へのトラックバックURL
https://fanblogs.jp/tb/6956248
※ブログオーナーが承認したトラックバックのみ表示されます。
この記事へのトラックバック