Last Update 2023/11/29
概要
関数のオーバーロード
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から変更
演算子のオーバーロード宣言を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から変更
演算子のオーバーロード宣言を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
GCC-12.2.0
GNU C Library 2.36
GNU Binutils 2.39
コード例・出力内容中の表記
・実行例中の太字表記部分は、コマンドなどの入力された文字列を示します。
・「︙」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。
・「︙」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。