4 番目、演算子のオーバーロード
1.コンセプト
C++ では演算子が関数に名前を付けることができます
string a = "hello";
a += "world"; // +(a, "world");变成helloworld
cout<< "hello"; // <<(cout, "hello");把hello输出给对象cout,即打印hello到屏幕
オーバーロード可能な演算子
オーバーロード不可能な演算子
実現形態
- メンバー関数 (最初のパラメーターはオブジェクトへの参照です)
class Time{
public:
Time operator+(Time &t);//例13
private:
int hour;
int min;
int sec;
};
Time a,b;
Time c = a+b;
- Friend 関数 (左オペランドはそれ自体ではなく、交換可能)
class Time{
public:
friend void operator<<(ostream, Time);//例21
private:
int hour;
int min;
int sec;
};
Time a;
std::out<<a;
上記の場合、タイマーの機能が実現されます。C 言語の場合、加算と減算の要件は同じデータ型 (int など) であり、時、分、秒の 10 進法の問題を解決する必要がありますが、C++ ではキーワードを使用できます。 (オペレーター)時間と分を避けるため 二塁問題への不安。
オペレーターの分類
- 数学演算子
+ - * / ++ --
- 関係演算子
== >= <= !=
- 特殊な演算子
[]
= 赋值运算符
() 仿函数
<< 输出运算符
2. 通常の演算子(数学演算子 + 関係演算子)
例 12. 通常のタイマー
#include <stdio.h>
#include <unistd.h>/*sleep()函数的头文件*/
class Timer{
public:
Timer()
{
hour = 23;
min = 59;
sec = 57;
}
~Timer()
{
}
void addtimer(int sec=1)
{
this->hour = (this->hour + ((this->min + (this->sec+sec)/60)/60))%24;
this->min = (this->min + (this->sec+sec)/60)%60;
this->sec = (this->sec+sec)%60;
}
void show()
{
printf("%2d:%2d:%2d\n", hour, min, sec);
}
private:
int hour;
int min;
int sec;
};
int main()
{
Timer t;
while(1)
{
t.addtimer(1);
t.show();
sleep(1);
}
}
操作結果:
@ubuntu:/mnt/hgfs/ub2$ g++ mytimer.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
23:59:58
23:59:59
0: 0: 0
0: 0: 1
0: 0: 2
0: 0: 3
0: 0: 4
0: 0: 5
0: 0: 6
0: 0: 7
0: 0: 8
0: 0: 9
^C
@ubuntu:/mnt/hgfs/ub2$
例 13、[オブジェクト] 演算子+([オブジェクト] &x)
ここで、次のような要件があります。まず、タイマーであるだけでなく、時間の重ね合わせをリアルタイムで監視する必要があります (たとえば、次のようなシナリオがあります: センサー 1 は 3 秒ごとにデータを監視する必要があり、センサー 2 はデータを監視する必要があります) 5 秒ごとにデータを監視する必要があり、その後 1 つの機器で 2 つのセンサーの合計経過時間を監視する必要があります)
ケース 1 の考え方によれば、次のように書くことができます。
#include <stdio.h>
#include <unistd.h>
class Timer{
public:
Timer()
{
hour = 23;
min = 59;
sec = 57;
}
~Timer()
{
}
void addtimer(int sec=1)
{
this->hour = (this->hour + ((this->min + (this->sec+sec)/60)/60))%24;
this->min = (this->min + (this->sec+sec)/60)%60;
this->sec = (this->sec+sec)%60;
}
void show()
{
printf("%2d:%2d:%2d\n", hour, min, sec);
}
private:
int hour;
int min;
int sec;
};
int main()
{
Timer t;
t.addtimer(3);
Timer t1;
t1.addtimer(5);
Timer t2;
t2 = t+t1;
t2.show();
#if 0
while(1)
{
t.addtimer(1);
t.show();
sleep(1);
}
#endif
}
操作結果:
@ubuntu:/mnt/hgfs/ub2$ g++ mytimer2_err.cpp
mytimer2_err.cpp: In function ‘int main()’:
mytimer2_err.cpp:45:11: error: no match for ‘operator+’ (operand types are ‘Timer’ and ‘Timer’)
t2 = t+t1;
~^~~
@ubuntu:/mnt/hgfs/ub2$
当然のことながら、C言語の考え方によれば、Timerクラスは構造体型のようなものであり、構造体のような型の場合、メンバーのデータ型は様々(int、charなど)であるため、共通の型としてみなすべきではありません。単純な加算と減算を実行します。
この問題を C 言語で解決するのはさらに面倒ですが、次のように C++ で簡単に実現できます。
#include <stdio.h>
#include <unistd.h>
class Timer{
public:
Timer()
{
hour = 0;
min = 0;
sec = 0;
}
~Timer()
{
}
void addtimer(int sec=1)
{
this->hour = (this->hour + ((this->min + (this->sec+sec)/60)/60))%24;
this->min = (this->min + (this->sec+sec)/60)%60;
this->sec = (this->sec+sec)%60;
}
void show()
{
printf("%2d:%2d:%2d\n", hour, min, sec);
}
//t2 = t+t1 operator是重载关键字,代表自身
//前Timer表示(t2)返回类型,后Timer表示所加的对象的类型,&x是引用的意思
Timer operator+(Timer &x)
{
Timer tem;
tem.sec = sec+x.sec;
tem.min = min+x.min;
tem.hour = hour+x.hour;
return tem;
}
private:
int hour;
int min;
int sec;
};
int main()
{
Timer t;
t.addtimer(3);
t.show();
Timer t1;
t1.addtimer(5);
t1.show();
Timer t2;
t2 = t + t1;
t2.show();
}
操作結果:
@ubuntu:/mnt/hgfs/ub2$ g++ mytimer2.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
0: 0: 3
0: 0: 5
0: 0: 8
@ubuntu:/mnt/hgfs/ub2$
上の例では、t2 = t+t1は Timer = Timer + Timer と同等です。
t3 = t2+5、つまり、Timer = Timer + 定数の場合、どのように対処すればよいでしょうか?
例 14、[オブジェクト] 演算子+(int [定数])
実際、理由は同じです。次のように、対応するオーバーロードされた関数を記述するだけです。
#include <stdio.h>
#include <unistd.h>
class Timer{
public:
Timer()
{
hour = 0;
min = 0;
sec = 0;
}
~Timer()
{
}
void addtimer(int sec=1)
{
this->hour = (this->hour + ((this->min + (this->sec+sec)/60)/60))%24;
this->min = (this->min + (this->sec+sec)/60)%60;
this->sec = (this->sec+sec)%60;
}
void show()
{
printf("%2d:%2d:%2d\n", hour, min, sec);
}
//t2 = t+t1 operator是重载关键字,代表自身
//前Timer表示(t2)返回类型,后Timer表示所加的对象的类型,&x是引用的意思
Timer operator+(Timer &x)
{
Timer tem;
tem.sec = sec+x.sec;
tem.min = min+x.min;
tem.hour = hour+x.hour;
return tem;
}
//t3 = t2+5
Timer operator+(int sec)
{
Timer tem;
tem.sec = this->sec+sec;
return tem;
}
private:
int hour;
int min;
int sec;
};
int main()
{
Timer t;
t.addtimer(3);
t.show();
Timer t1;
t1.addtimer(5);
t1.show();
Timer t2;
t2 = t + t1;
t2.show();
Timer t3;
t3 = t2+5;
t3.show();
}
操作結果:
@ubuntu:/mnt/hgfs/ub2$ g++ mytimer3.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
0: 0: 3
0: 0: 5
0: 0: 8
0: 0:13
@ubuntu:/mnt/hgfs/ub2$
例 15-1、後置加算 (i++)
#include <stdio.h>
#include <unistd.h>
class Timer{
public:
Timer()
{
hour = 0;
min = 0;
sec = 0;
}
~Timer()
{
}
void addtimer(int sec=1)
{
this->hour = (this->hour + ((this->min + (this->sec+sec)/60)/60))%24;
this->min = (this->min + (this->sec+sec)/60)%60;
this->sec = (this->sec+sec)%60;
}
void show()
{
printf("%2d:%2d:%2d\n", hour, min, sec);
}
//t2 = t+t1 operator是重载关键字,代表自身
//前Timer表示(t2)返回类型,后Timer表示所加的对象的类型,&x是引用的意思
Timer operator+(Timer &x)
{
Timer tem;
tem.sec = sec+x.sec;
tem.min = min+x.min;
tem.hour = hour+x.hour;
return tem;
}
//t3 = t2+5
Timer operator+(int sec)
{
Timer tem;
tem.sec = this->sec+sec;
return tem;
}
//t3 = t2++
Timer operator++(int)
{
Timer tem= *this;
sec++;
return tem;
}
private:
int hour;
int min;
int sec;
};
int main()
{
Timer t;
t.addtimer(3);
t.show();
Timer t1;
t1.addtimer(5);
t1.show();
Timer t2;
t2 = t + t1;
t2.show();
Timer t3;
t3 = t2++;
t3.show();
t2.show();
}
操作結果:
@ubuntu:/mnt/hgfs/ub2$ g++ houjiajia.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
0: 0: 3
0: 0: 5
0: 0: 8
0: 0: 8
0: 0: 9
@ubuntu:/mnt/hgfs/ub2$
例15-2 前置加算(++i)
#include <stdio.h>
#include <unistd.h>
class Timer{
public:
Timer()
{
hour = 0;
min = 0;
sec = 0;
}
~Timer()
{
}
void addtimer(int sec=1)
{
this->hour = (this->hour + ((this->min + (this->sec+sec)/60)/60))%24;
this->min = (this->min + (this->sec+sec)/60)%60;
this->sec = (this->sec+sec)%60;
}
void show()
{
printf("%2d:%2d:%2d\n", hour, min, sec);
}
//t2 = t+t1 operator是重载关键字,代表自身
//前Timer表示(t2)返回类型,后Timer表示所加的对象的类型,&x是引用的意思
Timer operator+(Timer &x)
{
Timer tem;
tem.sec = sec+x.sec;
tem.min = min+x.min;
tem.hour = hour+x.hour;
return tem;
}
//t3 = t2+5
Timer operator+(int sec)
{
Timer tem;
tem.sec = this->sec+sec;
return tem;
}
//t3 = ++t2
Timer operator++()
{
sec++;
return *this;
}
private:
int hour;
int min;
int sec;
};
int main()
{
Timer t;
t.addtimer(3);
t.show();
Timer t1;
t1.addtimer(5);
t1.show();
Timer t2;
t2 = t + t1;
t2.show();
Timer t3;
t3 = ++t2;
t3.show();
t2.show();
}
操作結果:
@ubuntu:/mnt/hgfs/ub2$ g++ qianjiajia.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
0: 0: 3
0: 0: 5
0: 0: 8
0: 0: 9
0: 0: 9
@ubuntu:/mnt/hgfs/ub2$
例16. 関係演算子、等価判定(==)
#include <stdio.h>
#include <unistd.h>
class Timer{
public:
Timer()
{
hour = 0;
min = 0;
sec = 0;
}
~Timer()
{
}
void addtimer(int sec=1)
{
this->hour = (this->hour + ((this->min + (this->sec+sec)/60)/60))%24;
this->min = (this->min + (this->sec+sec)/60)%60;
this->sec = (this->sec+sec)%60;
}
void show()
{
printf("%2d:%2d:%2d\n", hour, min, sec);
}
bool operator==(Timer &x)
{
if(sec==x.sec && min==x.min && hour==x.hour)
return true;
return false;
}
private:
int hour;
int min;
int sec;
};
int main()
{
Timer t;
t.addtimer(3);
t.show();
Timer t1;
t1.addtimer(5);
t1.show();
if(t==t1)
{
printf("OK\n");
}
else
{
printf("ERR\n");
}
Timer t2;
t2.addtimer(3);
t.show();
t2.show();
if(t==t2)
{
printf("OK\n");
}
else
{
printf("ERR\n");
}
}
操作結果:
@ubuntu:/mnt/hgfs/ub2$ g++ mytimer4_1.cpp
@ubuntu:/mnt/hgfs$ ./a.out
0: 0: 3
0: 0: 5
ERR
0: 0: 3
0: 0: 3
OK
@ubuntu:/mnt/hgfs/ub2$
3. 特殊な演算子
例 17、特殊演算子 [] の使用
次のように、時、分、秒を配列の形式で個別に出力したいとします。
printf("hour: %d\n", t[0]);
printf("min: %d\n", t[1]);
printf("sec: %d\n", t[2]);
それで、あります
#include <stdio.h>
#include <unistd.h>
class Timer{
public:
Timer()
{
hour = 23;
min = 59;
sec = 0;
}
~Timer()
{
}
void addtimer(int sec=1)
{
this->hour = (this->hour + ((this->min + (this->sec+sec)/60)/60))%24;
this->min = (this->min + (this->sec+sec)/60)%60;
this->sec = (this->sec+sec)%60;
}
void show()
{
printf("%2d:%2d:%2d\n", hour, min, sec);
}
int operator[](int i)
{
switch(i)
{
case 0:
return hour;
case 1:
return min;
case 2:
return sec;
}
}
private:
int hour;
int min;
int sec;
};
int main()
{
Timer t;
t.addtimer(3);
t.show();
printf("hour: %d\n", t[0]);
printf("min: %d\n", t[1]);
printf("sec: %d\n", t[2]);
}
操作結果:
@ubuntu:/mnt/hgfs/ub2$ g++ mytimer5_1.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
23:59: 3
hour: 23
min: 59
sec: 3
@ubuntu:/mnt/hgfs/ub2$
次に、最小値 59 を 30 に変更するなど、パラメーターを変更したいと思います。
#include <stdio.h>
#include <unistd.h>
class Timer{
public:
Timer()
{
hour = 23;
min = 59;
sec = 0;
}
~Timer()
{
}
void addtimer(int sec=1)
{
this->hour = (this->hour + ((this->min + (this->sec+sec)/60)/60))%24;
this->min = (this->min + (this->sec+sec)/60)%60;
this->sec = (this->sec+sec)%60;
}
void show()
{
printf("%2d:%2d:%2d\n", hour, min, sec);
}
int operator[](int i)
{
switch(i)
{
case 0:
return hour;
case 1:
return min;
case 2:
return sec;
}
}
private:
int hour;
int min;
int sec;
};
int main()
{
Timer t;
t.addtimer(3);
t.show();
printf("hour: %d\n", t[0]);
printf("min: %d\n", t[1]);
printf("sec: %d\n", t[2]);
t[1] = 30;
printf("hour: %d\n", t[0]);
printf("min: %d\n", t[1]);
printf("sec: %d\n", t[2]);
}
エラーは次のとおりです
@ubuntu:/mnt/hgfs/ub2$ g++ mytimer5_2.cpp
mytimer5_2.cpp: In function ‘int main()’:
mytimer5_2.cpp:58:12: error: lvalue required as left operand of assignment
t[1] = 30;
^~
@ubuntu:/mnt/hgfs/ub2$
これは、この場合 lvalues が参照できないためです。そのため、int Operator[](int i)をint &operator[](int i)に変更することで、パラメーターの変更を行うことができます。次のように
#include <stdio.h>
#include <unistd.h>
class Timer{
public:
Timer()
{
hour = 23;
min = 59;
sec = 0;
}
~Timer()
{
}
void addtimer(int sec=1)
{
this->hour = (this->hour + ((this->min + (this->sec+sec)/60)/60))%24;
this->min = (this->min + (this->sec+sec)/60)%60;
this->sec = (this->sec+sec)%60;
}
void show()
{
printf("%2d:%2d:%2d\n", hour, min, sec);
}
int &operator[](int i)
{
switch(i)
{
case 0:
return hour;
case 1:
return min;
case 2:
return sec;
}
}
private:
int hour;
int min;
int sec;
};
int main()
{
Timer t;
t.addtimer(3);
t.show();
printf("hour: %d\n", t[0]);
printf("min: %d\n", t[1]);
printf("sec: %d\n", t[2]);
t[1] = 30;
printf("hour: %d\n", t[0]);
printf("min: %d\n", t[1]);
printf("sec: %d\n", t[2]);
}
操作結果:
@ubuntu:/mnt/hgfs/ub2$ g++ mytimer5_2.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
23:59: 3
hour: 23
min: 59
sec: 3
hour: 23
min: 30
sec: 3
@ubuntu:/mnt/hgfs/ub2$
要約すると、オーバーロードがオブジェクトを返す場合、値を変更する権利を失います。つまり、lvalue 機能を失いますが、reference メソッドを使用することで lvalue 機能を取得できるということです。
例 18. 代入演算子のオーバーロード
前の例 8-3のディープ コピーでは、 A y = x;ディープ コピーを実現できますが、これはこの形式にのみ適用できます。直接的に表現したい場合は、y = xと表現すると、デフォルトで次のようになります。代入演算変数の浅いコピー
#include <stdio.h>
#include <string.h>
class A{
public:
A()
{
printf("A()\n");
p = new char[10];
strcpy(p, "hello");
}
A(const A &x)
{
printf("const A()\n");
p = new char[10];
strcpy(p, x.p);
}
~A()
{
printf("~A()\n");
delete [] p;
}
private:
char *p;
};
int main()
{
A x;
A y = x;
y = x;
}
コンパイルは問題ありませんが、実行するとエラーが報告されます
@ubuntu:/mnt/hgfs/ub2$ g++ class_copy1_err.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
A()
const A()
~A()
~A()
free(): double free detected in tcache 2
Aborted (core dumped)
@ubuntu:/mnt/hgfs/ub2$
深いコピーと浅いコピーの問題を解決するには、代入演算子のオーバーロードが便利です
#include <stdio.h>
#include <string.h>
class A{
public:
A()
{
printf("A()\n");
p = new char[10];
strcpy(p, "hello");
}
A(const A &x)
{
printf("const A()\n");
p = new char[10];
strcpy(p, x.p);
}
~A()
{
printf("~A()\n");
delete [] p;
}
A & operator=(A &x)
{
printf("operator=\n");
p = new char[10];
strcpy(p, x.p);
return *this;
}
private:
char *p;
};
int main()
{
A x;
A y = x;
y = x;
}
演算結果
@ubuntu:/mnt/hgfs/ub2$ g++ class_copy1.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
A()
const A()
operator=
~A()
~A()
@ubuntu:/mnt/hgfs/ub2$
例 19、出力情報の印刷
#include <iostream>
int main()
{
std::cout << "hello";
}
ヘッダファイルはiostream(入出力ストリーム、ioストリーム)、cout(文字出力、文字出力)の実行結果です。
@ubuntu:/mnt/hgfs/ub2$ g++ converter1_1.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
hello@ubuntu:/mnt/hgfs/ub2$
改行はendlです
#include <iostream>
int main()
{
std::cout << "hello"<<std::endl;
}
演算結果
@ubuntu:/mnt/hgfs/ub2$ g++ converter1_2.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
hello
@ubuntu:/mnt/hgfs/ub2$
重複したものが見つかるはずです: std
std 名前空間に、演算子 **<<** のオーバーロードをサポートし、対応するコード関数をサポートするオブジェクト cout と endl があることを示します。
したがって、最初に名前空間 std を決定してから、hello print を実行できます。
#include <iostream>
using namespace std;
int main()
{
cout << "hello"<<endl;
}
結果は前の例と同じです
@ubuntu:/mnt/hgfs/ub2$ g++ converter1_3.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
hello
@ubuntu:/mnt/hgfs/ub2$
例 20. 人民元為替レート決済のシミュレーション (ファンクターの適用)
#include <iostream>
using namespace std;
class Converter{
public:
Converter(double rate)
{
this->rate = rate;
}
double operator()(double rmb)
{
return rmb*rate;
}
private:
double rate;
};
double RMBto(double rmb, double rate)
{
return rmb*rate;
}
int main()
{
// std::cout << "hello"<<std::endl;
// cout << "hello"<<endl;
Converter RMBtoUS(6.4);
cout << RMBtoUS(10) << endl;
cout << RMBtoUS(10) << endl;
cout << RMBtoUS(10) << endl;
cout << RMBtoUS(10) << endl;
cout << RMBtoUS(10) << endl;
cout << RMBtoUS(10) << endl;
cout << RMBtoUS(10) << endl;
cout << RMBtoUS(10) << endl;
Converter RMBtoE(8.4);
cout << RMBtoE(100) << endl;
cout << RMBtoE(100) << endl;
cout << RMBtoE(100) << endl;
cout << RMBtoE(100) << endl;
cout << RMBtoE(100) << endl;
cout << RMBtoE(100) << endl;
}
演算結果
@ubuntu:/mnt/hgfs/ub2$ g++ converter2.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
64
64
64
64
64
64
64
64
840
840
840
840
840
840
@ubuntu:/mnt/hgfs/ub2$
例21、cout<<【内容】
Example 17を振り返ると、以前の印刷方法はオブジェクトに適用してからオブジェクトのメンバーにアクセスして印刷すること、つまり printf() が使用されていることがわかります。
class Timer{
...
void show()
{
printf("%2d:%2d:%2d\n", hour, min, sec);
}
...
}
...
...
Timer t;
t.show();
これはcout<<[content]の方法で実装されます(例 17の mytimer5_2.cpp に基づいています)。
#include <stdio.h>
#include <unistd.h>
#include <iostream>
using namespace std;
class Timer{
public:
Timer()
{
hour = 23;
min = 59;
sec = 0;
}
~Timer()
{
}
void addtimer(int sec=1)
{
this->hour = (this->hour + ((this->min + (this->sec+sec)/60)/60))%24;
this->min = (this->min + (this->sec+sec)/60)%60;
this->sec = (this->sec+sec)%60;
}
void show()
{
printf("%2d:%2d:%2d\n", hour, min, sec);
}
int &operator[](int i)
{
switch(i)
{
case 0:
return hour;
case 1:
return min;
case 2:
return sec;
}
}
friend ostream &operator<<(ostream &out, const Timer &t);//友元函数式
private:
int hour;
int min;
int sec;
};
ostream &operator<<(ostream &out, const Timer &t)
{
out << "hour: "<<t.hour << " min: "<<t.min<<" sec: "<<t.sec<< endl;
}
int main()
{
Timer t;
t.addtimer(3);
t.show();
printf("hour: %d\n", t[0]);
printf("min: %d\n", t[1]);
printf("sec: %d\n", t[2]);
t[1] = 30;
printf("hour: %d\n", t[0]);
printf("min: %d\n", t[1]);
printf("sec: %d\n", t[2]);
cout << t;
}
演算結果
@ubuntu:/mnt/hgfs/ub2$ g++ mytimer6_1.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
23:59: 3
hour: 23
min: 59
sec: 3
hour: 23
min: 30
sec: 3
hour: 23 min: 30 sec: 3
@ubuntu:/mnt/hgfs/ub2$