Loose-Info.com
Last Update 2023/11/29
TOP - 各種テスト - C++ - オーバーロード

概要

関数のオーバーロード
演算子のオーバーロードの引数・戻り値に関する挙動確認
メンバ関数および非メンバ関数による演算子のオーバーロード
単項演算子のオーバーロード
二項演算子のオーバーロード
代入演算子のオーバーロード
添字演算子のオーバーロード
メンバアクセス演算子のオーバーロード
関数呼び出し演算子のオーバーロード
前置・後置インクリメント演算子のオーバーロード


関数のオーバーロード


sample.cpp
#include <iostream> struct Smpl { void func() { std::cout << "Smpl::func()" << std::endl; } // 引数が1つのintで名前が同じ関数 void func(int n) { std::cout << "Smpl::func(int " << n << ")" << std::endl; } // 引数が1つのdoubleで名前が同じ関数 void func(double d) { std::cout << "Smpl::func(double " << d << ")" << std::endl; } // 引数が2つのintで名前が同じ関数 void func(int n, double d) { std::cout << "Smpl::func(int " << n << ", double " << d << ")" << std::endl; } // 引数がintとint&で名前が同じ関数 void func(int n, int& r) { std::cout << "Smpl::func(int " << n << ", int& " << r << ")" << std::endl; } // 引数がintとint&&で名前が同じ関数 void func(int n, int&& r) { std::cout << "Smpl::func(int " << n << ", int&& " << r << ")" << std::endl; } // 引数が1つのint型へのポインタで名前が同じ関数 void func(int *p) { std::cout << "Smpl::func(int * " << *p << ")" << std::endl; } }; int main() { int n = 100; Smpl s; s.func(); s.func(1); s.func(2.2); s.func(3, 3.3); s.func(4, n); s.func(5, 6); s.func(&n); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out Smpl::func() Smpl::func(int 1) Smpl::func(double 2.2) Smpl::func(int 3, double 3.3) Smpl::func(int 4, int& 100) Smpl::func(int 5, int&& 6) Smpl::func(int * 100)

演算子のオーバーロードの引数・戻り値に関する挙動確認


sample1.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } class Smpl { int n; public: Smpl(int n0 = 0) : n(n0) {} ~Smpl() { std::cout << this << " : Destructor" << std::endl; } // 演算子「+」のオーバーロード // 引数 : 参照 // 戻り値 : 参照 Smpl& operator+(const Smpl& rs) { n = n + rs.n; return *this; } void view(); }; void Smpl::view() { std::cout << std::dec; std::cout << "n = " << n << std::endl; } int main() { Smpl s1(1); Smpl s2(2); Smpl s3; s3 = s1 + s2; s3.view(); std::cout << "&s1 = " << &s1 << std::endl; std::cout << "&s2 = " << &s2 << std::endl; std::cout << "&s3 = " << &s3 << std::endl; byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); byteseq(reinterpret_cast<unsigned char *>(&s3), sizeof(s3), "s3"); std::cout << std::endl; s3 = s1 + s1 + s2 + s2; s3.view(); std::cout << "&s1 = " << &s1 << std::endl; std::cout << "&s2 = " << &s2 << std::endl; std::cout << "&s3 = " << &s3 << std::endl; byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); byteseq(reinterpret_cast<unsigned char *>(&s3), sizeof(s3), "s3"); std::cout << std::endl; }

実行結果
$ gcc -Wall sample1.cpp -lstdc++ $ ./a.out n = 3 &s1 = 0x7ffd73d3343c &s2 = 0x7ffd73d33438 &s3 = 0x7ffd73d33434 s1 = 03 00 00 00 <--- 演算子関数呼び出し対象となるオブジェクトの値も変更される s2 = 02 00 00 00 s3 = 03 00 00 00 n = 10 &s1 = 0x7ffd73d3343c &s2 = 0x7ffd73d33438 &s3 = 0x7ffd73d33434 s1 = 0A 00 00 00 <--- 演算子関数呼び出し対象となるオブジェクトの値も変更される s2 = 02 00 00 00 s3 = 0A 00 00 00 0x7ffd73d33434 : Destructor 0x7ffd73d33438 : Destructor 0x7ffd73d3343c : Destructor

sample2.cpp
演算子のオーバーロード宣言をsample1.cppから変更
// 演算子「+」のオーバーロード // 引数 : 参照 // 戻り値 : 値 Smpl operator+(const Smpl& rs) { return Smpl(n + rs.n); }

実行結果
$ gcc -Wall sample2.cpp -lstdc++ $ ./a.out 0x7ffed8ede540 : Destructor n = 3 &s1 = 0x7ffed8ede53c &s2 = 0x7ffed8ede538 &s3 = 0x7ffed8ede534 s1 = 01 00 00 00 <--- 演算子関数呼び出し対象となるオブジェクトの値は変更されない s2 = 02 00 00 00 s3 = 03 00 00 00 0x7ffed8ede544 : Destructor 0x7ffed8ede548 : Destructor 0x7ffed8ede54c : Destructor n = 6 &s1 = 0x7ffed8ede53c &s2 = 0x7ffed8ede538 &s3 = 0x7ffed8ede534 s1 = 01 00 00 00 <--- 演算子関数呼び出し対象となるオブジェクトの値は変更されない s2 = 02 00 00 00 s3 = 06 00 00 00 0x7ffed8ede534 : Destructor 0x7ffed8ede538 : Destructor 0x7ffed8ede53c : Destructor

sample3.cpp
演算子のオーバーロード宣言をsample2.cppから変更
// 演算子「+」のオーバーロード // 引数 : 値 // 戻り値 : 値 Smpl operator+(const Smpl s) { return Smpl(n + s.n); }

実行結果
$ gcc -Wall sample3.cpp -lstdc++ $ ./a.out 0x7ffdf1aa0270 : Destructor 0x7ffdf1aa0274 : Destructor n = 3 &s1 = 0x7ffdf1aa026c &s2 = 0x7ffdf1aa0268 &s3 = 0x7ffdf1aa0264 s1 = 01 00 00 00 s2 = 02 00 00 00 s3 = 03 00 00 00 0x7ffdf1aa0278 : Destructor 0x7ffdf1aa0280 : Destructor 0x7ffdf1aa0288 : Destructor 0x7ffdf1aa028c : Destructor 0x7ffdf1aa0284 : Destructor 0x7ffdf1aa027c : Destructor n = 6 &s1 = 0x7ffdf1aa026c &s2 = 0x7ffdf1aa0268 &s3 = 0x7ffdf1aa0264 s1 = 01 00 00 00 s2 = 02 00 00 00 s3 = 06 00 00 00 0x7ffdf1aa0264 : Destructor 0x7ffdf1aa0268 : Destructor 0x7ffdf1aa026c : Destructor

メンバ関数および非メンバ関数による演算子のオーバーロード


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } class Smpl { int n; public: Smpl(int n0 = 0) : n(n0) {} ~Smpl() { std::cout << this << " : Destructor" << std::endl; } // メンバ関数として、オーバーロードする演算子を定義 Smpl& operator+=(const Smpl& rs) { n = n + rs.n; return *this; } void view() { std::cout << std::dec; std::cout << "n = " << n << std::endl; } }; // 非メンバ関数として、オーバーロードする演算子を定義 Smpl operator+(const Smpl& rs1, const Smpl& rs2) { Smpl s = rs1; std::cout << "op+ s = " << &s << std::endl; return s += rs2; } int main() { Smpl s1(1); Smpl s2(2); Smpl s3; std::cout << "初期状態の出力" << std::endl; std::cout << "&s1 = " << &s1 << std::endl; std::cout << "&s2 = " << &s2 << std::endl; std::cout << "&s3 = " << &s3 << std::endl; byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); byteseq(reinterpret_cast<unsigned char *>(&s3), sizeof(s3), "s3"); std::cout << std::endl; std::cout << "メンバ関数である演算子関数(+=)の呼び出し" << std::endl; s2 += s1; s2.view(); std::cout << "&s1 = " << &s1 << std::endl; std::cout << "&s2 = " << &s2 << std::endl; std::cout << "&s3 = " << &s3 << std::endl; byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); byteseq(reinterpret_cast<unsigned char *>(&s3), sizeof(s3), "s3"); std::cout << std::endl; std::cout << "非メンバ関数である演算子関数(+)の呼び出し" << std::endl; (s1 + s2 + s1).view(); std::cout << "&s1 = " << &s1 << std::endl; std::cout << "&s2 = " << &s2 << std::endl; std::cout << "&s3 = " << &s3 << std::endl; byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); byteseq(reinterpret_cast<unsigned char *>(&s3), sizeof(s3), "s3"); std::cout << std::endl; std::cout << "非メンバ関数である演算子関数(+)の呼び出し(結果をs3に代入)" << std::endl; s3 = s1 + s2 + s1 + s2; s3.view(); std::cout << "&s1 = " << &s1 << std::endl; std::cout << "&s2 = " << &s2 << std::endl; std::cout << "&s3 = " << &s3 << std::endl; byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); byteseq(reinterpret_cast<unsigned char *>(&s3), sizeof(s3), "s3"); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 初期状態の出力 &s1 = 0x7ffe1c096838 &s2 = 0x7ffe1c096834 &s3 = 0x7ffe1c096830 s1 = 01 00 00 00 s2 = 02 00 00 00 s3 = 00 00 00 00 メンバ関数である演算子関数(+=)の呼び出し n = 3 &s1 = 0x7ffe1c096838 &s2 = 0x7ffe1c096834 &s3 = 0x7ffe1c096830 s1 = 01 00 00 00 s2 = 03 00 00 00 s3 = 00 00 00 00 非メンバ関数である演算子関数(+)の呼び出し op+ s = 0x7ffe1c09680c 0x7ffe1c09680c : Destructor op+ s = 0x7ffe1c09680c 0x7ffe1c09680c : Destructor n = 5 0x7ffe1c09683c : Destructor 0x7ffe1c096840 : Destructor &s1 = 0x7ffe1c096838 &s2 = 0x7ffe1c096834 &s3 = 0x7ffe1c096830 s1 = 01 00 00 00 s2 = 03 00 00 00 s3 = 00 00 00 00 非メンバ関数である演算子関数(+)の呼び出し(結果をs3に代入) op+ s = 0x7ffe1c09680c 0x7ffe1c09680c : Destructor op+ s = 0x7ffe1c09680c 0x7ffe1c09680c : Destructor op+ s = 0x7ffe1c09680c 0x7ffe1c09680c : Destructor 0x7ffe1c096844 : Destructor 0x7ffe1c096848 : Destructor 0x7ffe1c09684c : Destructor n = 8 &s1 = 0x7ffe1c096838 &s2 = 0x7ffe1c096834 &s3 = 0x7ffe1c096830 s1 = 01 00 00 00 s2 = 03 00 00 00 s3 = 08 00 00 00 0x7ffe1c096830 : Destructor 0x7ffe1c096834 : Destructor 0x7ffe1c096838 : Destructor

単項演算子のオーバーロード


sample.cpp
#include <iostream> class Smpl { int n; public: Smpl(int n0 = 0) : n(n0) {} ~Smpl() { std::cout << this << " : Destructor" << std::endl; } // 単項演算子のオーバーロード Smpl operator-() const { Smpl s = *this; std::cout << "op- s = " << &s << std::endl; s.n = -s.n; return s; } void view() { std::cout << std::dec; std::cout << "n = " << n << std::endl; } }; int main() { Smpl s1(1); Smpl s2(2); Smpl s3; std::cout << "演算子関数の呼び出し前" << std::endl; std::cout << "s1."; s1.view(); std::cout << "s2."; s2.view(); std::cout << "s3."; s3.view(); std::cout << std::endl; std::cout << "演算子関数(単項-)の呼び出し -s1" << std::endl; (-s1).view(); std::cout << "s1."; s1.view(); std::cout << "s2."; s2.view(); std::cout << "s3."; s3.view(); std::cout << std::endl; std::cout << "演算子関数(単項-)の呼び出し s3 = -s1" << std::endl; s3 = -s1; std::cout << "s1."; s1.view(); std::cout << "s2."; s2.view(); std::cout << "s3."; s3.view(); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 演算子関数の呼び出し前 s1.n = 1 s2.n = 2 s3.n = 0 演算子関数(単項-)の呼び出し -s1 op- s = 0x7fff448a9098 n = -1 0x7fff448a9098 : Destructor s1.n = 1 s2.n = 2 s3.n = 0 演算子関数(単項-)の呼び出し s3 = -s1 op- s = 0x7fff448a909c 0x7fff448a909c : Destructor s1.n = 1 s2.n = 2 s3.n = -1 0x7fff448a908c : Destructor 0x7fff448a9090 : Destructor 0x7fff448a9094 : Destructor

二項演算子のオーバーロード


sample.cpp
#include <iostream> class Smpl { int n; public: Smpl(int n0 = 0) : n(n0) {} ~Smpl() { std::cout << this << " : Destructor" << std::endl; } // 二項演算子のオーバーロード Smpl operator+(Smpl& s0) { Smpl s = *this; std::cout << "op+ s = " << &s << std::endl; s.n = s.n + s0.n; return s; } void view() { std::cout << std::dec; std::cout << "n = " << n << std::endl; } }; int main() { Smpl s1(1); Smpl s2(2); Smpl s3; std::cout << "演算子関数の呼び出し前" << std::endl; std::cout << "s1."; s1.view(); std::cout << "s2."; s2.view(); std::cout << "s3."; s3.view(); std::cout << std::endl; std::cout << "演算子関数(二項+)の呼び出し s1 + s2" << std::endl; (s1 + s2).view(); std::cout << "s1."; s1.view(); std::cout << "s2."; s2.view(); std::cout << "s3."; s3.view(); std::cout << std::endl; std::cout << "演算子関数(単項-)の呼び出し s3 = s1 + s2" << std::endl; s3 = s1 + s2; std::cout << "s1."; s1.view(); std::cout << "s2."; s2.view(); std::cout << "s3."; s3.view(); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 演算子関数の呼び出し前 s1.n = 1 s2.n = 2 s3.n = 0 演算子関数(二項+)の呼び出し s1 + s2 op+ s = 0x7fff68a28518 n = 3 0x7fff68a28518 : Destructor s1.n = 1 s2.n = 2 s3.n = 0 演算子関数(単項-)の呼び出し s3 = s1 + s2 op+ s = 0x7fff68a2851c 0x7fff68a2851c : Destructor s1.n = 1 s2.n = 2 s3.n = 3 0x7fff68a2850c : Destructor 0x7fff68a28510 : Destructor 0x7fff68a28514 : Destructor

代入演算子のオーバーロード


sample.cpp
#include <iostream> class Smpl { int nlen; unsigned short *pu; public: Smpl(int n, unsigned short *p) : nlen(n) { pu = new unsigned short[nlen]; for(int i=0; i<nlen; i++) { pu[i] = p[i]; } } ~Smpl() { std::cout << this << " : Destructor" << std::endl; delete [] pu; } // 代入演算子のオーバーロード Smpl& operator=(const Smpl& rs) { if (this != &rs) { delete [] pu; nlen = rs.nlen; pu = new unsigned short[nlen]; for(int i=0; i<nlen; i++) { pu[i] = rs.pu[i]; } } return *this; } void view() { std::cout << std::dec; std::cout << "nlen = " << nlen << std::endl; for (int i=0; i<nlen; i++) { std::cout << std::dec; std::cout << " [" << i << "] = " << pu[i] << std::endl; } } }; int main() { unsigned short us[2]{1, 2}; Smpl s1(2, us); Smpl s2(0, nullptr); std::cout << "演算子関数の呼び出し前" << std::endl; std::cout << "s1."; s1.view(); std::cout << "s2."; s2.view(); std::cout << std::endl; std::cout << "演算子関数(代入)の呼び出し s2 = s1" << std::endl; s2 = s1; std::cout << "s1."; s1.view(); std::cout << "s2."; s2.view(); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 演算子関数の呼び出し前 s1.nlen = 2 [0] = 1 [1] = 2 s2.nlen = 0 演算子関数(代入)の呼び出し s2 = s1 s1.nlen = 2 [0] = 1 [1] = 2 s2.nlen = 2 [0] = 1 [1] = 2 0x7fffd1a2c8e0 : Destructor 0x7fffd1a2c8f0 : Destructor

添字演算子のオーバーロード


sample.cpp
#include <iostream> class Smpl { int nlen; unsigned short *pu; public: Smpl(int n, unsigned short *p) : nlen(n) { pu = new unsigned short[nlen]; for(int i=0; i<nlen; i++) { pu[i] = p[i]; } } ~Smpl() { std::cout << this << " : Destructor" << std::endl; delete [] pu; } // 添字演算子のオーバーロード unsigned short operator[](int n) const { if ((0 <= n) && (n < nlen)) { return (*this).pu[n]; } else { return 0xFFFF; } } void view() { std::cout << std::dec; std::cout << "nlen = " << nlen << std::endl; for (int i=0; i<nlen; i++) { std::cout << std::dec; std::cout << " [" << i << "] = " << pu[i] << std::endl; } } }; int main() { unsigned short us[3]{1, 2, 3}; Smpl s(3, us); std::cout << "演算子関数の呼び出し前" << std::endl; std::cout << "s."; s.view(); std::cout << std::endl; std::cout << "演算子関数(添字)の呼び出し s[2]" << std::endl; std::cout << "s[2] = " << s[2] << std::endl; std::cout << std::endl; std::cout << "演算子関数(添字)の呼び出し s[5]" << std::endl; std::cout << "s[5] = " << s[5] << std::endl; std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 演算子関数の呼び出し前 s.nlen = 3 [0] = 1 [1] = 2 [2] = 3 演算子関数(添字)の呼び出し s[2] s[2] = 3 演算子関数(添字)の呼び出し s[5] s[5] = 65535 0x7ffd1ff12a80 : Destructor

メンバアクセス演算子のオーバーロード


sample.cpp
#include <iostream> struct Smpl0 { int n1{1}; int n2{2}; }; class Smpl { Smpl0 st; public: ~Smpl() { std::cout << this << " : Destructor" << std::endl; } // メンバアクセス演算子のオーバーロード Smpl0 *operator->() { return &st; } }; int main() { Smpl s; std::cout << "演算子関数(メンバアクセス)の呼び出し s->n1" << std::endl; std::cout << s->n1 << std::endl; std::cout << "演算子関数(メンバアクセス)の呼び出し s->n2" << std::endl; std::cout << s->n2 << std::endl; std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 演算子関数(メンバアクセス)の呼び出し s->n1 1 演算子関数(メンバアクセス)の呼び出し s->n2 2 0x7fffaf3b36e8 : Destructor

関数呼び出し演算子のオーバーロード


sample.cpp
#include <iostream> class Smpl { int n{1}; public: ~Smpl() { std::cout << this << " : Destructor" << std::endl; } // 関数呼び出し演算子のオーバーロード Smpl& operator()(int n0) { n += n0; std::cout << "n = " << n << std::endl; return *this; } }; int main() { Smpl s; std::cout << "演算子関数(関数呼び出し)の呼び出し s(1)" << std::endl; s(1); std::cout << "演算子関数(関数呼び出し)の呼び出し s(2)" << std::endl; s(2); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 演算子関数(関数呼び出し)の呼び出し s(1) n = 2 演算子関数(関数呼び出し)の呼び出し s(2) n = 4 0x7ffc59d56adc : Destructor

前置・後置インクリメント演算子のオーバーロード


sample.cpp
#include <iostream> class Smpl { int n{1}; public: ~Smpl() { std::cout << this << " : Destructor" << std::endl; } // 前置インクリメント演算子のオーバーロード Smpl& operator++() { n++; std::cout << "n = " << n << std::endl; return *this; } // 後置インクリメント演算子のオーバーロード Smpl operator++(int) { Smpl s = *this; ++*this; std::cout << "n = " << n << std::endl; return s; } }; int main() { Smpl s; std::cout << "演算子関数(前置インクリメント)の呼び出し ++s" << std::endl; ++s; std::cout << "演算子関数(後置インクリメント)の呼び出し s++" << std::endl; s++; std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 演算子関数(前置インクリメント)の呼び出し ++s n = 2 演算子関数(後置インクリメント)の呼び出し s++ n = 3 n = 3 0x7ffdd75dd05c : Destructor 0x7ffdd75dd058 : Destructor

実行環境

GNU bash, version 5.1.16
GCC-12.2.0
GNU C Library 2.36
GNU Binutils 2.39


コード例・出力内容中の表記

・実行例中の太字表記部分は、コマンドなどの入力された文字列を示します。
・「」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。