作成日
2014/12/06最終更新
2021/09/07記事区分
一般公開基本的な代入演算子
#include <iostream>
#include <algorithm>
using namespace std;
class MyClass {
public:
MyClass(int size);
~MyClass();
void operator=(const MyClass& other); // 演算子オーバーロード
public:
int Get(int i);
private:
int m_size;
int* m_intarr;
};
MyClass::MyClass(int size) {
m_size = size;
m_intarr = new int[size];
fill_n(m_intarr, size, 0);
}
MyClass::~MyClass() {
delete[] m_intarr;
}
void MyClass::operator=(const MyClass& other) {
/* 手法 1 */
/* 別変数にnewしてからdelete */
/* - メリット: newに失敗してももとの値は保持される */
/* - デメリット: 解放せずにnewするため失敗しやすくなる */
int* intarr = new int[other.m_size];
delete[] m_intarr; // メモリリークしないように、格納していた値の領域を解放
m_intarr = intarr;
m_size = other.m_size;
copy(other.m_intarr, other.m_intarr + m_size, m_intarr);
/* 手法 2 */
/* ヌルポインタを代入してデストラクタでの delete[] に備える */
/* (ヌルポインタ p を delete p してもエラーにならないという */
/* 古いコンパイラは未対応の比較的新しいC++の仕様) */
/* - メリット: 解放してからnewするため成功しやすい */
/* - デメリット: newに失敗しても元の値は戻せない */
// delete[] m_intarr;
// m_intarr = NULL;
// m_size = 0;
// m_intarr = new int[other.m_size];
// m_size = other.m_size;
// copy(other.m_intarr, other.m_intarr + m_size, m_intarr);
/* だめな手法 */
/* newで失敗すると解放済みなためデストラクタでエラー */
// delete[] m_intarr;
// m_intarr = new int[other.m_size];
// m_size = other.m_size;
// copy(other.m_intarr, other.m_intarr + m_size, m_intarr);
}
int MyClass::Get(int i) {
return m_intarr[i];
}
int main() {
MyClass obj(5);
MyClass obj2(5);
obj2 = obj; // オーバーロードした演算子
cout << obj2.Get(0) << endl;
return 0;
}
[] 演算子
#include <iostream>
using namespace std;
class MyClass {
public:
static const int SIZE = 8; // 静的メンバ定数
public: // [] 演算子のオーバーロード
int& operator[](int i); // 非constオブジェクト用
const int& operator[](int i) const; // constオブジェクト用
private:
const int& At_(int i) const;
private:
int m_intarr[SIZE];
};
int& MyClass::operator[](int i) {
return const_cast<int&>(At_(i)); // constを外す危険なキャスト
}
const int& MyClass::operator[](int i) const {
return At_(i);
}
const int& MyClass::At_(int i) const {
return m_intarr[i];
}
int main() {
MyClass obj;
cin >> obj[0];
cout << obj[0] << endl;
return 0;
}
キャスト演算子
#include <iostream>
using namespace std;
class MyClass {
public: // キャスト演算子のオーバーロード
operator int() const; // [] 演算子と異なり非constオブジェクトとconstオブジェクトで
operator double() const; // constメンバ関数を使い回しても問題にならない。代入などしない。
};
MyClass::operator int() const {
return 2; // サンプルのため値は仮
}
MyClass::operator double() const {
return 2.2; // サンプルのため値は仮
}
int main() {
MyClass obj;
int intval = obj;
cout << intval << endl; //=> 2
cout << (double)obj << endl; //=> 2.2
return 0;
}
二項演算子
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass(int intval);
public: // 二項演算子のオーバーロード (自分以外に相手を必要とする演算子)
MyClass operator/(const MyClass& rop) const; // rop: right operand
MyClass operator-(const MyClass& rop) const;
public:
operator int() const;
private:
int m_intval;
};
MyClass::MyClass(int intval) :
m_intval(intval)
{
}
MyClass MyClass::operator/(const MyClass& rop) const {
return MyClass(m_intval / rop.m_intval); // テンポラリオブジェクト:
}
MyClass MyClass::operator-(const MyClass& rop) const {
return MyClass(m_intval - rop.m_intval);
}
MyClass::operator int() const {
return m_intval;
}
int main() {
MyClass objA(10);
MyClass objB(2);
MyClass objC = objA / objB;
cout << (int)objC << endl; //=> 5
cout << objA - objB << endl; //=> 8 (暗黙のキャスト)
return 0;
}
単項演算子
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass(int intval);
public: // 単項演算子のオーバーロード
MyClass operator-() const;
public:
operator int() const;
private:
int m_intval;
};
MyClass::MyClass(int intval) :
m_intval(intval)
{
}
MyClass MyClass::operator-() const {
return MyClass(-m_intval);
}
MyClass::operator int() const {
return m_intval;
}
int main() {
MyClass obj(5);
cout << -obj << endl; //=> -5
return 0;
}
複合代入演算子
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass(int intval);
public: // 複合代入演算子のオーバーロード
MyClass& operator+=(const MyClass& rop); // 非constオブジェクトだけを考えてよい
public:
operator int() const;
private:
int m_intval;
};
MyClass::MyClass(int intval) :
m_intval(intval)
{
}
MyClass& MyClass::operator+=(const MyClass& rop) {
m_intval += rop.m_intval;
return *this; // this: 自分自身を指すポインタ
}
MyClass::operator int() const {
return m_intval;
}
int main() {
MyClass objA(2);
MyClass objB(20);
cout << (int)(objA += objB) << endl; //=> 22
return 0;
}
インクリメント/デクリメント演算子
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass(int intval);
public: // インクリメント演算子のオーバーロード
MyClass& operator++(); // 前置
MyClass operator++(int); // 後置 (int: 前置と区別するため。仕様)
public:
operator int() const;
private:
int m_intval;
};
MyClass::MyClass(int intval) :
m_intval(intval)
{
}
MyClass& MyClass::operator++() {
++m_intval;
return *this;
}
MyClass MyClass::operator++(int) {
MyClass copy = *this;
++m_intval;
return copy;
}
MyClass::operator int() const {
return m_intval;
}
int main() {
MyClass obj(0);
cout << ++obj << endl; //=> 1
cout << obj << endl; //=> 1
cout << obj++ << endl; //=> 1 (2でないことがポイントですね)
cout << obj << endl; //=> 2
return 0;
}
二項演算子の補足事項
rop または lop が MyClass でない場合は工夫が必要です。
#include <iostream>
using namespace std;
class MyClass {
friend MyClass operator+(int lop, const MyClass& rop); // クラス内でプロトタイプ宣言
public:
MyClass(int intval);
public:
MyClass operator+(const MyClass& rop) const;
public:
operator int() const;
private:
int m_intval;
};
MyClass::MyClass(int intval) :
m_intval(intval)
{
}
MyClass MyClass::operator+(const MyClass& rop) const {
return MyClass(m_intval + rop.m_intval);
}
MyClass::operator int() const {
return m_intval;
}
MyClass operator+(int lop, const MyClass& rop) { // フレンド関数 (非メンバ関数)
cout << "FRIEND" << endl;
return MyClass(lop + rop.m_intval); // private メンバにアクセスできる
}
int main() {
MyClass obj(1);
// 1. キャストで解決
// ↓(MyClass)10 は MyClass(10) に相当します。仕様です
cout << obj + (MyClass)10 << endl; //=> 11
cout << (MyClass)10 + obj << endl; //=> 11
MyClass _obj = 10; // 参考: 暗黙のキャスト
MyClass __obj = (MyClass)10; // と解釈されます。コピーコンストラクタの出番
// 2. フレンド関数で解決
cout << 10 + obj << endl; //=> "FRIEND\n11"
return 0;
}
暗黙のキャストを禁止 (参考)
#include <iostream>
using namespace std;
class MyClass {
public:
// explicit: 暗黙のキャストを禁止する
// 引数が 1 つになり得るコンストラクタに付与可能
explicit MyClass(int intval);
private:
int m_intval;
};
MyClass::MyClass(int intval) :
m_intval(intval)
{
}
int main() {
MyClass obj = 0; // エラーにできます
// MyClass obj = (MyClass)0; // 暗黙のキャストが許される場合
// MyClass obj = MyClass(0); // ↑はつまり ← (コピーコンストラクタ)
// MyClass obj(MyClass(0)); // ↑は ← としても文法上は同じ
return 0;
}
<< 演算子
フレンド関数を用いた非メンバ関数型の演算子オーバーロードを利用して << 演算子をオーバーロードできます。
#include <iostream>
using namespace std;
class MyClass {
friend ostream& operator<<(ostream& ostr, const MyClass& rop);
public:
MyClass(int intval);
private:
int m_intval;
};
MyClass::MyClass(int intval) :
m_intval(intval)
{
}
ostream& operator<<(ostream& ostr, const MyClass& rop) {
return ostr << rop.m_intval;
}
int main() {
MyClass obj(-1);
cout << obj << endl; //=> -1
return 0;
}
関連記事
- ダウンキャスト (C++をもう一度)実行時型情報 RTTI #include <iostream> #include <typeinfo> using namespace std; class MyClass { public: virtual ~MyClass() {} // typeid で正しい RTTI // (RunTime Type Information; 実行時型情報) ...
- 競技プログラミングの基本処理チートシート (C++)限られた時間の中で問題を解くために必要となる、競技プログラミングにおける基本的な処理のチートシートです。競プロにおけるメジャー言語 C++ を利用します。その際 C++11 の機能は利用せず C++03 の機能の範囲内で記述します。 頻度高く定期的に開催されるコンテスト AtCoder Codeforces main.cpp #include <iostream>
- 構造体と列挙体 (C++をもう一度)構造体 #include <iostream> using namespace std; struct MyStruct { char charval; int intval; }; void Show(MyStruct* obj) { cout << obj->intval << endl; } int main() { ...
- Valgrind による C/C++ メモリリーク検出JVM メモリリークでは JDK の jstat や jmap で原因を調査できます。C/C++ では valgrind の Memcheck ツールが利用できます。valgrind には複数のツールが含まれており既定のツールが Memcheck です。他のツールを利用する場合は --tool オプションで指定します。 [簡単な利用例](h
- クラスの基本/初期化 (C++をもう一度)構造体のように初期化する (非推奨) #include <iostream> using namespace std; const int MAX_STR = 16; class MyClass { public: int m_integer; char m_str[MAX_STR + 1]; void Show(); }; void MyClass::Show...