新規記事の投稿を行うことで、非表示にすることが可能です。
2017年11月05日
《その106》 静的データメンバ,静的メンバ関数,演算子関数(p.473演習13-1)
次のプログラムは、新版明解C++入門編 p.469 〜 p.472 にある日付に関するプログラムです。
静的データメンバ static int dmax[];
静的メンバ関数 static int days_of_month(int y, int m);
static bool leap_year(int year)
は、特定のオブジェクトではなくクラス全体に関わるデータ(各月の日数)または作業(各月の日数を調べる、閏年かどうかを調べる)です。
このようなデータや作業は、静的データメンバ・静的メンバ関数で実現するべきです。
------------------------------------------------
// Date.h
#ifndef ___Class_Date
#define ___Class_Date
#include <string>
#include <iostream>
class Date {
int y; // 西暦年
int m; // 月
int d; // 日
static int dmax[]; // 各月の日数(静的データメンバ)
static int days_of_month(int y, int m); // y年m月の日数(静的メンバ関数)
public:
Date(); // デフォルトコンストラクタ
Date(int yy, int mm = 1, int dd = 1); // コンストラクタ
static bool leap_year(int year) { // 閏年なら true を返却(静的メンバ関数)
return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
}
int year() const { return y; } // 年を返却
int month() const { return m; } // 月を返却
int day() const { return d; } // 日を返却
bool leap_year() const { // 閏年なら true を返却(非静的メンバ関数)
return leap_year(y);
}
Date preceding_day() const; // 前日の日付を返却
Date following_day() const; // 翌日の日付を返却
int day_of_year() const; // 年内の経過日数を返却
int day_of_week() const; // 曜日を返却
std::string to_string() const; // 文字列表現を返却
};
std::ostream& operator<<(std::ostream& s, const Date& x); // 挿入子の多重定義
#endif
// Date.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <ctime>
#include <sstream>
#include <iostream>
#include "Date.h"
using namespace std;
// 各月の日数(閏年でない年)
int Date::dmax[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
// y年m月の日数
int Date::days_of_month(int y, int m) {
return dmax[m - 1] + (leap_year(y) && m == 2);
}
// デフォルトコンストラクタ(今日の日付で作成)
Date::Date() {
time_t current = time(NULL); // 現在の暦時刻を取得
struct tm* local = localtime(&current); // 要素別の時刻に変換
y = local->tm_year + 1900; // 年:tm_yearは西暦年-1900
m = local->tm_mon + 1; // 月:tm_monは0〜11
d = local->tm_mday;
}
// コンストラクタ(指定した年月日で作成)
Date::Date(int yy, int mm, int dd) {
y = yy;
m = mm;
d = dd;
}
// 年内の経過日数
int Date::day_of_year() const {
int days = d;
for (int i = 1; i < m; i++)
days += days_of_month(y, i);
return days;
}
// 前日の日付
Date Date::preceding_day() const {
Date temp = *this;
if (temp.d > 1)
temp.d--;
else {
if (--temp.m < 1) {
temp.y--;
temp.m = 12;
}
temp.d = days_of_month(temp.y, temp.m);
}
return temp;
}
// 翌日の日付
Date Date::following_day() const {
Date temp = *this;
if (temp.d < days_of_month(temp.y, temp.m))
temp.d++;
else {
if (++temp.m > 12) {
temp.y++;
temp.m = 1;
}
temp.d = 1;
}
return temp;
}
// 曜日(日曜〜土曜が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;
}
// 文字列表現を返却
string Date::to_string() const {
ostringstream s;
s << y << "年" << m << "月" << d << "日";
return s.str();
}
// 挿入子の多重定義
ostream& operator<<(ostream& s, const Date& x) {
return s << x.to_string();
}
// DateTest.cpp
#include <iostream>
#include "Date.h"
using namespace std;
int main() {
Date today; // 今日の日付
cout << "今 日の日付:" << today << '\n';
cout << "昨 日の日付:" << today.preceding_day() << '\n';
cout << "一昨日の日付:" << today.preceding_day().preceding_day() << '\n';
cout << "明 日の日付:" << today.following_day() << '\n';
cout << "明後日の日付:" << today.following_day().following_day() << '\n';
cout << "元旦から" << today.day_of_year() << "日経過しています。\n";
cout << "今年は閏年"
<< (today.leap_year() ? "です。" : "ではありません。") << '\n';
int y, m, d;
cout << "西暦年:"; cin >> y;
cout << "その年は閏年"
<< (Date::leap_year(y) ? "です。" : "ではありません。") << '\n';
}
------------------------------------------------
新版明解C++入門編 p.473 演習13-1
上記の日付クラス Date.h に対して、以下の演算子関数を追加した日付クラスを作成せよ。
● 二つの日付が等しいかどうかを判定する等価演算子==
● 二つの日付が等しくないかどうかを判定する等価演算子!=
● 二つの日付の大小関係を判定する関係演算子>, >=, <, <=
※ より新しい日付のほうを大きいと判定すること。
● 二つの日付の減算を行う(何日離れているかを求める)減算演算子−
※ 左オペランドから右オペランドを引くこと(日付差を正負の値で示す)。
● 日付を翌日の日付に更新する前置および後置の増分演算子++
● 日付を前日の日付に更新する前置および後置の増分演算子−−
● 日付を n 日進めた日付に更新する複合代入演算子+=
● 日付を n 日戻した日付に更新する複合代入演算子−=
● 日付の n 日後の日付を求める加算演算子+
● 日付の n 日前の日付を求める減算演算子−
// Date.h
#ifndef ___Class_Date
#define ___Class_Date
#include <string>
#include <iostream>
class Date {
int y; // 西暦年
int m; // 月
int d; // 日
static int dmax[]; // 各月の日数(静的データメンバ)
static int days_of_month(int y, int m); // y年m月の日数(静的メンバ関数)
static int day_of_year(int y, int m, int d); // y年1月1日〜y年m月d日までの日数(静的メンバ関数)
public:
Date(); // デフォルトコンストラクタ
Date(int yy, int mm = 1, int dd = 1); // コンストラクタ
static bool leap_year(int year) { // 閏年なら true を返却(静的メンバ関数)
return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
}
int year() const { return y; } // 年を返却
int month() const { return m; } // 月を返却
int day() const { return d; } // 日を返却
bool leap_year() const { // 閏年なら true を返却(非静的メンバ関数)
return leap_year(y);
}
Date preceding_day() const; // 前日の日付を返却
Date following_day() const; // 翌日の日付を返却
int day_of_year() const; // 年内の経過日数を返却
int day_of_week() const; // 曜日を返却
friend bool operator==(const Date& p, const Date& q);
friend bool operator!=(const Date& p, const Date& q);
friend bool operator <(const Date& p, const Date& q);
friend bool operator<=(const Date& p, const Date& q);
friend bool operator >(const Date& p, const Date& q);
friend bool operator>=(const Date& p, const Date& q);
friend int operator-(const Date& p, const Date& q);
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
Date& operator+=(int n);
Date& operator-=(int n);
Date operator+(int n);
Date operator-(int n);
std::string to_string() const;
};
std::ostream& operator<<(std::ostream& s, const Date& x);
#endif
// Date.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <ctime>
#include <sstream>
#include <iomanip>
#include <iostream>
#include "Date.h"
using namespace std;
int Date::dmax[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int Date::days_of_month(int y, int m) {
return dmax[m - 1] + (leap_year(y) && m == 2);
}
int Date::day_of_year(int y, int m, int d) {
int days = d;
for (int i = 1; i < m; i++)
days += days_of_month(y, i);
return days;
}
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;
}
Date::Date(int yy, int mm, int dd) {
y = yy;
m = mm;
d = dd;
}
int Date::day_of_year() const {
return day_of_year(y, m, d);
}
Date Date::preceding_day() const {
Date temp = *this;
if (temp.d > 1)
temp.d--;
else {
if (--temp.m < 1) {
temp.y--;
temp.m = 12;
}
temp.d = days_of_month(temp.y, temp.m);
}
return temp;
}
Date Date::following_day() const {
Date temp = *this;
if (temp.d < days_of_month(temp.y, temp.m))
temp.d++;
else {
if (++temp.m > 12) {
temp.y++;
temp.m = 1;
}
temp.d = 1;
}
return temp;
}
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;
}
bool operator== (const Date& p, const Date& q) {
return p.y == q.y && p.m == q.m && p.d == q.d;
}
bool operator!=(const Date& p, const Date& q) {
return !(p == q);
}
bool operator<(const Date& p, const Date& q) {
if (p.y == q.y)
return p.day_of_year() < q.day_of_year();
else
return p.y < q.y;
}
bool operator<=(const Date& p, const Date& q) {
return p < q || p == q;
}
bool operator>(const Date& p, const Date& q) {
return !(p <= q);
}
bool operator>=(const Date& p, const Date& q) {
return !(p < q);
}
int operator-(const Date& p, const Date& q) {
int delta_d = p.day_of_year() - q.day_of_year();
if (p.y > q.y) {
for (int i = q.y; i < p.y; i++)
delta_d += (365 + Date::leap_year(i));
}
if (p.y < q.y) {
for (int i = p.y; i < q.y; i++)
delta_d -= (365 + Date::leap_year(i));
}
return delta_d;
}
Date& Date::operator++() {
*this = following_day();
return *this;
}
Date Date::operator++(int) {
Date temp = *this;
*this = following_day();
return temp;
}
Date& Date::operator--() {
*this = preceding_day();
return *this;
}
Date Date::operator--(int) {
Date temp = *this;
*this = preceding_day();
return temp;
}
Date& Date::operator+=(int n) {
for (int i = 0; i < n; i++)
++*this;
return *this;
}
Date& Date::operator-=(int n) {
for (int i = 0; i < n; i++)
--*this;
return *this;
}
Date Date::operator+(int n) {
Date temp = *this;
for (int i = 0; i < n; i++)
++temp;
return temp;
}
Date Date::operator-(int n) {
Date temp = *this;
for (int i = 0; i < n; i++)
--temp;
return temp;
}
string Date::to_string() const {
ostringstream s;
s << y << "年" << setw(2) << m << "月" << setw(2) << d << "日";
return s.str();
}
ostream& operator<<(ostream& s, const Date& x) {
return s << x.to_string();
}
// DateTest.cpp
#include <iomanip>
#include <iostream>
#include "Date.h"
using namespace std;
int main()
{
/*
Date today;
cout << "今 日の日付:" << today << '\n';
cout << "昨 日の日付:" << today.preceding_day() << '\n';
cout << "一昨日の日付:" << today.preceding_day().preceding_day() << '\n';
cout << "明 日の日付:" << today.following_day() << '\n';
cout << "明後日の日付:" << today.following_day().following_day() << '\n';
cout << "元旦から" << today.day_of_year() << "日経過しています。\n";
cout << "今年は閏年"
<< (today.leap_year() ? "です。" : "ではありません。") << '\n';
int y, m, d;
cout << "西暦年:"; cin >> y;
cout << "その年は閏年"
<< (Date::leap_year(y) ? "です。" : "ではありません。") << '\n';
*/
Date d0; cout << "d0 … " << d0 << '\n';
Date d1(2017, 11, 5); cout << "d1 … " << d1 << '\n';
Date d2(2016, 11, 4); cout << "d2 … " << d2 << '\n';
Date d3(2016, 11, 6); cout << "d3 … " << d3 << '\n';
Date d4(2017, 11, 5); cout << "d4 … " << d4 << '\n';
Date d5(2015, 11, 4); cout << "d5 … " << d5 << '\n';
Date d6(2015, 11, 6); cout << "d6 … " << d6 << '\n';
cout << boolalpha;
cout << "d0 == d1 … " << (d0 == d1) << '\n';
cout << "d0 != d1 … " << (d0 != d1) << '\n';
cout << "d0 == d2 … " << (d0 == d2) << '\n';
cout << "d0 != d2 … " << (d0 != d2) << '\n';
cout << "d0 < d1 … " << (d0 < d1) << '\n';
cout << "d0 <= d1 … " << (d0 <= d1) << '\n';
cout << "d0 > d1 … " << (d0 > d1) << '\n';
cout << "d0 >= d1 … " << (d0 >= d1) << '\n';
cout << "d0 < d2 … " << (d0 < d2) << '\n';
cout << "d0 <= d2 … " << (d0 <= d2) << '\n';
cout << "d0 > d2 … " << (d0 > d2) << '\n';
cout << "d0 >= d2 … " << (d0 >= d2) << '\n';
cout << "d1 - d2 … " << setw(4) << (d1 - d2) << '\n';
cout << "d2 - d1 … " << setw(4) << (d2 - d1) << '\n';
cout << "d1 - d3 … " << setw(4) << (d1 - d3) << '\n';
cout << "d3 - d1 … " << setw(4) << (d3 - d1) << '\n';
cout << "d1 - d5 … " << setw(4) << (d1 - d5) << '\n';
cout << "d5 - d1 … " << setw(4) << (d5 - d1) << '\n';
cout << "d1 - d6 … " << setw(4) << (d1 - d6) << '\n';
cout << "d6 - d1 … " << setw(4) << (d6 - d1) << '\n';
cout << "++d5 … " << ++d5 << '\n';
cout << "d5 … " << d5 << '\n';
cout << "d6++ … " << d6++ << '\n';
cout << "d6 … " << d6 << '\n';
cout << "--d5 … " << --d5 << '\n';
cout << "d5 … " << d5 << '\n';
cout << "d6-- … " << d6-- << '\n';
cout << "d6 … " << d6 << '\n';
cout << "d5 += 60 … " << (d5 += 60) << '\n';
cout << "d5 … " << d5 << '\n';
cout << "d5 -= 60 … " << (d5 -= 60) << '\n';
cout << "d5 … " << d5 << '\n';
cout << "d5 + 60 … " << (d5 + 60) << '\n';
cout << "d5 … " << d5 << '\n';
cout << "d5 - 60 … " << (d5 - 60) << '\n';
cout << "d5 … " << d5 << '\n';
}
--
2017年11月03日
《その105》 静的データメンバ
静的データメンバ
静的データメンバというのは、クラス定義の中で、static付きで宣言されたデータメンバのことです。
static付きデータメンバの実体は、個々のクラスオブジェクトごとに作られるのではなく、メモリ空間の中にただ1つだけ作られます。
静的データメンバの値は、コンストラクタから参照することも、個々のオブジェクトから参照することも可能です。
静的データメンバは、プログラムの開始時に作成されます。
ただし、static付きの静的データメンバは、宣言時に初期化することができません。
作成された静的データメンバに初期値を与えてその実体を定義する場所は、ファイル有効範囲(クラス定義や関数定義の外)でなければいけません。
以下の Humanクラスは、データメンバとして
static int counter; // カウンタ(静的データメンバ)
int id_no; // 識別番号
std::string name; // 氏名
std::string country; // 国籍
double height; // 身長(cm)
double weight; // 体重(kg)
を持っています。
static付きで宣言されたデータメンバ int counter が、静的データメンバです。
静的データメンバは、 Humanクラス型の全てのオブジェクトで共有されます。
// Human.h
#include <string>
#include <iostream>
class Human {
private:
static int counter;
int id_no;
std::string name; // 氏名
std::string country; // 国籍
double height; // 身長(cm)
double weight; // 体重(kg)
public:
Human(std::string n, std::string c, double h, double w);
static int get_counter() { return counter; }
int id() const;
std::string namae() const { return name; }
std::string kokuseki() const { return country; }
double shinchou() const { return height; }
double taijuu() const { return weight; }
double bmi() const; // 肥満指数BMI
double standard_w() const; // 標準体重(kg)
};
inline std::ostream& operator<<(std::ostream& s, const Human& x) {
return s << "No." << x.id() << " 氏名:" << x.namae();
}
// Human.cpp
#include <iostream>
#include "Human.h"
int Human::counter = 0;
Human::Human(std::string n, std::string c, double h, double w)
: name(n), country(c), height(h), weight(w) {
id_no = ++counter;
}
int Human::id() const{ return id_no; }
double Human::bmi() const {
return weight / height / height * 10000;
}
double Human::standard_w() const {
return height * height * 22 / 10000;
}
// HumanTest.cpp
#include <iostream>
#include <string>
#include "Human.h"
using namespace std;
int main() {
Human saitou("斉藤とまと", "日本", 168.5, 55.0);
Human sakata("坂田メロン", "日本", 154.8, 50.5);
Human nakano("中野いちご", "日本", 161.0, 53.0);
cout << saitou << '\n';
cout << sakata << '\n';
cout << nakano << '\n';
cout << "現時点の counter値は " << Human::get_counter() << " です。\n";
}
この例では、コンストラクタが、++counter によってインクリメントされた counter値を、クラスオブジェクトの作成順に与えています。したがって、
最初に作成された saitouクラスは counter = 1
2番目に作られた sakataクラスは counter = 2
3番目に作られた nakanoクラスは counter = 3
になります。
これまでに何番までの識別番号が使われたのかを、静的データメンバ counter が保持してくれています。
static int counter
の実体は一つだけで、クラスオブジェクトが作られるたびにその値がインクリメントされます。そして、全てのクラスオブジェクトからその値を参照することができます。
上のプログラムで、静的データメンバ counter の値を参照するメンバ関数は、
static int get_counter() { return counter; }
です。
static付きの関数は、静的メンバ関数と呼ばれます。静的メンバ関数も、静的データメンバと同じで、クラスオブジェクト全体で共有します。
--
《その104》 演算子関数の定義例
データメンバが整数値一つだけの、下記のクラス TinyInt(新版明解C++入門編 p.457)に注釈を加えて自分なりに復習してみました。
// TinyInt.h
------------------------------------------------
#include <climits>
#include <iostream>
class TinyInt {
int v; // テータメンバは整数値一つのみ。
public:
// コンストラクタ(int型から TinyInt型への変換コンストラクタ兼用)
TinyInt(int value = 0) : v(value) { }
// intへの変換関数
// (ユーザ定義変換 … この変換関数 と 上の変換コンストラクタ の総称)
operator int() const { return v; }
// 論理否定演算子! (v == 0 なら true, v != 0 なら false)
bool operator!() const { return v == 0; }
// 前置増分演算子++
// インクリメント後の自分自身への参照 TinyInt& を返却するので、
// 返却値に対して この演算子 ++ を連続使用することができる。
// 例:TinyInt t;
// ++(++t); // t は 2 になる。
TinyInt& operator++() {
if (v < INT_MAX) v++;
return *this;
}
// 後置増分演算子++
// 前置増分演算子++ と同様にインクリメントするが、返却するのは
// インクリメント前の自分自身の値。
TinyInt operator++(int) {
TinyInt x = *this;
if (v < INT_MAX) v++;
return x;
}
// 2項+演算子(クラス内で定義したフレンド関数)
// 仮引数の型は const TinyInt&
// 演算結果である TinyInt型一時オブジェクトの値を返却する。
friend TinyInt operator+(const TinyInt& x, const TinyInt& y) {
return TinyInt(x.v + y.v);
}
// 複号代入+=演算子
// 自分自身への参照 TinyInt& を返却するので、
// 返却値に対して この演算子 += を連続使用することができる。
// 例:TinyInt t;
// (t += 5) += 10; // t は 15 になる。
TinyInt& operator+=(const TinyInt& x) { v += x.v; return *this; }
friend std::ostream& operator<<(std::ostream& s, const TinyInt& x) {
return s << x.v;
}
};
------------------------------------------------
--
2017年11月02日
《その103》 演算子関数の一般的な定義の形式(p.455演習12-3)
単項演算子関数と2項演算子関数の一般的な定義の形式を確認しておきます。
// ヘッダ--------------------------------
class C {
・・・
public:
・・・
C operator☆() { ・・・ }
C operator△(const C& x) { ・・・ }
・・・
};
inline C operator★(const C& x) { ・・・ }
inline C operator▲(const C& x, const C& y) { ・・・ }
// ------------------------------------
operator☆ は単項演算子関数(メンバ関数)
operator★ は単項演算子関数(非メンバ関数)
operator△ は2項演算子関数(メンバ関数)
operator▲ は2項演算子関数(非メンバ関数)
新版明解C++入門編 p.455 演習12-3
演習11-3(このブログの《その86》)で作成した時刻クラスに、各種の演算子関数を追加せよ。仕様などは自分で考えること。
// Time.h
#ifndef ___Class_Time
#define ___Class_Time
#include <iostream>
class Time {
int hour;
int minute;
int second;
public:
Time();
Time(int h, int m = 0, int s = 0);
int get_hour() const { return hour; }
int get_minute() const { return minute; }
int get_second() const { return second; }
// hour を進める。
void forward_h(int h);
// minute を進める。
void forward_m(int m);
// second を進める。
void forward_s(int s);
// 時・分・秒の値を調整。
void adjust(int &h, int &m, int &s);
// ○時間○分○秒後の時刻を求める。
Time& operator+=(const Time& x);
// ○時間○分○秒前の時刻を求める。
Time& operator-=(const Time& x);
// 時刻と時刻の時間差を求める。
friend Time operator-(const Time& x, const Time& y);
};
std::ostream& operator<<(std::ostream& so, const Time& x);
std::istream& operator>>(std::istream& si, Time& x);
#endif
// Time.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iomanip>
#include <ctime>
#include <sstream>
#include "Time.h"
using namespace std;
Time::Time() {
time_t current = time(NULL);
struct tm* local = localtime(¤t);
hour = local->tm_hour;
minute = local->tm_min;
second = local->tm_sec;
}
Time::Time(int h, int m, int s)
: hour(h), minute(m), second(s) {
adjust(h, m, s);
hour = h; minute = m; second = s;
}
void Time::forward_h(int h) {
hour = (hour + h) % 24;
}
void Time::forward_m(int m) {
m = minute + m;
adjust(hour, m, second);
}
void Time::forward_s(int s) {
s = second + s;
adjust(hour, minute, s);
}
void Time::adjust(int &h, int &m, int &s) {
while (s < 0)
s += 60, m--;
while (m < 0)
m += 60, h--;
while (h < 0)
h += 24;
m = m + s / 60;
s = s % 60;
h = h + m / 60;
m = m % 60;
h = h % 24;
hour = h; minute = m; second = s;
}
Time& Time::operator+=(const Time& x) {
this->adjust(hour += x.hour,
minute += x.minute,
second += x.second);
return *this;
}
Time& Time::operator-=(const Time& x) {
this->adjust(hour -= x.hour,
minute -= x.minute,
second -= x.second);
return *this;
}
Time operator-(const Time& x, const Time& y) {
Time temp(x.hour - y.hour,
x.minute - y.minute,
x.second - y.second
);
return temp;
}
ostream& operator<<(ostream& so, const Time& x) {
return so << setfill('0')
<< setw(2) << x.get_hour() << "時(時間)"
<< setw(2) << x.get_minute() << "分"
<< setw(2) << x.get_second() << "秒";
}
istream& operator>>(istream& si, Time& x) {
int h, m, s; char c;
si >> h >> c >> m >> c >> s;
x = Time(h, m, s);
return si;
}
// TimeTest.cpp
#include <iostream>
#include "Time.h"
using namespace std;
int main() {
Time a1;
Time a2 = a1;
Time a3 = a1;
cout << "現在時刻 : " << a1 << '\n';
/* 演算子関数とは無関係なので、ここではコメントアウトしておきます。
cout << '\n';
cout << "**:**:**形式で時刻を入力 : "; cin >> a;
cout << a << '\n';
a.forward_h(20); cout << a << "( 20時間進めました。)" << '\n';
a.forward_m(80); cout << a << "( 80分 進めました。)" << '\n';
a.forward_s(3700); cout << a << "( 3700秒進めました。)" << '\n';
*/
Time t1, t2, t3;
cout << "\n◆ **時間**分**秒後の時刻を求めます。\n";
cout << " **:**:**形式で時間を入力 … "; cin >> t1;
cout << (a1 += t1) << "です。\n";
cout << "\n◆ **時間**分**秒前の時刻を求めます。\n";
cout << " **:**:**形式で時間を入力 … "; cin >> t2;
cout << (a2 -= t2) << "です。\n";
cout << "\n◆ 現在時刻との時間差を求めます。\n" ;
cout << " **:**:**形式で時刻を入力 … "; cin >> t3;
cout << "それは " << t3 - a3 << " 後か、\n";
cout << "または " << a3 - t3 << " 前です。\n";
}
--
《その102》 複素数クラス と 演算子の多重定義
複素数クラス C ( 複素数 a + bi の実部 a と虚部 b は整数とする )を用いて、いろいろな演算子関数をまとめてみました。
これまでに学んできた次のような点にも注意しながら、プログラムを作成しました。
・変換コンストラクタの必要性。
・演算子関数の返却値を C にするか C& にするかの判断。
・演算子関数の仮引数を const C& にする理由。
・演算子関数をクラスのメンバ関数として定義する場合と、非メンバ関数として定義する場合の相違点。
------------------------------------
// Complex.h
#ifndef ___Class_Abc
#define ___Class_Abc
#include <iomanip>
#include <iostream>
class C {
int a_;
int b_;
public:
// コンストラクタ(変換コンストラクタ兼用)
C(int a = 0, int b = 0) : a_(a), b_(b) { }
// ゲッタ
int a() const { return a_; }
int b() const { return b_; }
// 単項+演算子
C operator+() const { return *this; }
// 単項-演算子
C operator-() const { return C(-a_, -b_); }
// 2項+演算子(クラス内で定義したフレンド関数)
friend C operator+(const C& x, const C& y) {
return C(x.a_ + y.a_, x.b_ + y.b_);
}
// 2項-演算子(クラス内で定義したフレンド関数)
friend C operator-(const C& x, const C& y) {
return C(x.a_ - y.a_, x.b_ - y.b_);
}
// 複号代入+=演算子
C& operator+=(const C x) {
a_ += x.a_; b_ += x.b_;
return *this;
}
// 複号代入-=演算子
C& operator-=(const C x) {
a_ -= x.a_; b_ -= x.b_;
return *this;
}
// 等価==演算子(クラス内で定義したフレンド関数)
friend bool operator==(const C& x, const C& y) {
return x.a_ == y.a_ && x.b_ == y.b_;
}
// 等価!=演算子(クラス内で定義したフレンド関数)
friend bool operator!=(const C& x, const C& y) {
return !(x == y);
}
};
/* ← メンバ関数との二重定義を避けるためのコメントアウト
// 2項+演算子(クラスの外部で定義した場合)
inline C operator+(const C& x, const C& y) {
return C(x.a() + y.a(), x.b() + y.b());
}
// 2項-演算子(クラスの外部で定義した場合)
inline C operator-(const C& x, const C& y) {
return C(x.a() - y.a(), x.b() - y.b());
}
// 等価==演算子(クラスの外部で定義した場合)
inline bool operator==(const C& x, const C& y) {
return x.a() == y.a() && x.b() == y.b();
}
// 等価!=演算子(クラスの外部で定義した場合)
inline bool operator!=(const C& x, const C& y) {
return !(x == y);
}
*/ ← コメントアウト ここまで
inline std::ostream& operator<<(std::ostream& s, const C& x) {
return s << "a … " << std::setw(2) << x.a() << ", b … " << std::setw(2) << x.b();
}
#endif
------------------------------------
------------------------------------
// ComplexTest
#include <iostream>
#include "Complex.h"
using namespace std;
int main() {
C c1;
C c2(10, 20);
C c3(1, 2);
cout << "\n【 複素数 a + bi 】\n";
cout << "c1 : " << c1 << '\n';
cout << "c2 : " << c2 << '\n';
cout << "c3 : " << c3 << '\n';
cout << "\n単項+演算子, 単項-演算子\n";
cout << "+c3 : " << +c3 << '\n';
cout << "-c3 : " << -c3 << '\n';
cout << "\n2項+演算子, 2項-演算子\n";
cout << "c2 + c3 : " << c2 + c3 << '\n';
cout << "c2 - c3 : " << c2 - c3 << '\n';
cout << "c2 + 10 : " << c2 + 10 << '\n';
cout << "c2 - 10 : " << c2 - 10 << '\n';
cout << " 1 + c3 : " << 1 + c3 << '\n';
cout << " 3 - c3 : " << 3 - c3 << '\n';
cout << "\n複号代入+=演算子, 複号代入-=演算子\n";
cout << "c2 += c3 : " << (c2 += c3) << '\n';
cout << "c2 += 10 : " << (c2 += 10) << '\n';
cout << "c2 -= c3 : " << (c2 -= c3) << '\n';
cout << "c2 -= 10 : " << (c2 -= 10) << '\n';
cout << "\n等価==演算子, 等価!=演算子\n";
cout << boolalpha
<< "c2 == c3 : " << (c2 == c3) << '\n';
cout << "c2 != c3 : " << (c2 != c3) << '\n';
cout << "c1 == 0 : " << (c1 == 0) << '\n';
}
------------------------------------
--
2017年11月01日
《その101》 "異なる型のオブジェクト" や "定数" への参照
"異なる型のオブジェクト" や "定数" への参照
異なる型のオブジェクトへの参照や、定数への参照は const でなけれはなりません。
例えば、次の例では (A), (B) はエラーになります。
(A)の場合は、int型の q は double型の b への参照。
また、(B)の場合は、int型の r は定数 3 への参照です。
このように、異なる型のオブジェクトへの参照や、定数への参照は const でないとエラーになります。
int a = 1;
double b = 2.0;
int& p = a; // (@)
int& q = b; // (A) エラー
int& r = 3; // (B) エラー
次のプログラムは、以上のことを踏まえて作ったものです。
#include <iostream>
using namespace std;
class C {
int x_;
public:
C(int x = 0) : x_(x) { } // ※ 変換コンストラクタ
operator int() const { return x_; } // ※ 変換関数
};
int main() {
int a = 1;
double b = 2.0;
int& p = a; // (@)
const int& q = b; // (A)
const int& r = 3; // (B)
const C& s = 4; // (C)
cout << p << '\n';
cout << q << '\n';
cout << r << '\n';
cout << s << '\n';
}
このプログラムの
const C& s = 4; // (C)
では、C型のクラス s にint型定数 4 を代入しています。
C型の変数への代入であれば、変換コンストラクタが働くので 全く問題ありません。
上の場合は、そうではなくて、C&型の s への代入です。
C& s = 4;
は定数 4 への参照ですから、異なるオブジェクトへの参照ということになり、
const C& s = 4;
でないとエラーになります。
--