モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

演算子オーバーロード (C++をもう一度)

モーダルを閉じる

ステッカーを選択してください

お支払い手続きへ
モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2014/12/06
最終更新最終更新
2021/09/07
記事区分記事区分
一般公開

目次

    低レイヤーのプログラミングとOS開発が趣味。C言語を使っています。

    基本的な代入演算子

    #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;
    }
    
    Likeボタン(off)0
    詳細設定を開く/閉じる
    アカウント プロフィール画像

    低レイヤーのプログラミングとOS開発が趣味。C言語を使っています。

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    >>さらに詳しくステッカーを贈る
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      ログインする

      関連記事