演算子

スポンサーリンク

オーバーロード可能な演算子

C++ では次の演算子がオーバーロード可能です。

+  -  *  /  %  ^  &amp;  |  ~  !  <<  >>  ++  --
=  +=  -=  *=  /=  %=  ^=  &amp;=  |=  <<=  >>=
==  !=  <  >  <=  >=  &&  ||  ,  ->*  ->  ()  []
new  delete  new[]  delete[]

メンバ関数としての定義

次のプログラムは,座標を表す Point クラスに演算子 + を定義した例です。

#include <iostream>

class Point
{
public:
    int x;
    int y;
    Point(int x, int y) : x(x), y(y) { }
    Point operator+(const Point& rhs);
};

// 演算子 + を定義
Point Point::operator+(const Point& rhs)
{
    return Point(this->x + rhs.x, this->y + rhs.y);
}

int main()
{
    Point p(2, 3);
    Point q(4, 5);
    Point r = p + q;   // p.operator+(q) と考える
    std::cout << r.x << std::endl;  // 出力: 6
    std::cout << r.y << std::endl;  // 出力: 8
    return 0;
}

演算子の定義は,関数の形式で次のように書きます。関数名は operator 演算子 とします。

// 単項演算子
返却型 operator 演算子 () { ... }

// 二項演算子
返却型 operator 演算子 ( 右オペランド) { ... }

引数名には,しばしば lhs (left-hand side), rhs (right-hand side) という名前が使われます。

グローバル関数としての定義

次のプログラムは,グローバル関数として演算子 + を定義した例です。

#include <iostream>

class Point
{
public:
    int x;
    int y;
    Point(int x, int y) : x(x), y(y) { }
};

// 演算子 + を定義
Point operator+(const Point& lhs, const Point& rhs)
{
    return Point(lhs.x + rhs.x, lhs.y + rhs.y);
}

int main()
{
    Point p(2, 3);
    Point q(4, 5);
    Point r = p + q;   // operator+(p, q) と考える
    std::cout << r.x << std::endl;  // 出力: 6
    std::cout << r.y << std::endl;  // 出力: 8
    return 0;
}

グローバル関数としてを定義する場合,演算子の定義は次のように書きます。

// 単項演算子
返却型 operator 演算子 ( オペランド) { ... }

// 二項演算子
返却型 operator 演算子 (1 左オペランド, 2 右オペランド) { ... }

出力演算子の定義

次のように出力演算子を定義すると,Point 型の値 p を std::cout << p で出力できるようになります。

#include <iostream>

class Point
{
public:
    int x;
    int y;
    Point(int x, int y) : x(x), y(y) { }
};

// 出力演算子を定義
std::ostream& operator<<(std::ostream& o, const Point& p)
{
    o << "(" << p.x << ", " << p.y << ")";
    return o;
}

int main()
{
    Point p(2, 3);
    std::cout << p << std::endl;  // 出力: (2, 3)
    return 0;
}

前置/後置インクリメント演算子

次のプログラムは,Point クラスに前置/後置インクリメント演算子を定義した例です。

#include <iostream>

class Point
{
public:
    int x;
    int y;
    Point(int x, int y) : x(x), y(y) { }
    Point& operator++();     // 前置
    Point operator++(int);   // 後置
};

// 前置インクリメント
Point& Point::operator++()
{
    ++this->x; ++this->y;
    return *this;
}

// 後置インクリメント
Point Point::operator++(int)
{
    Point ret(*this);
    ++this->x; ++this->y;
    return ret;
}

// 出力演算子
std::ostream& operator<<(std::ostream& o, const Point& p)
{
    o << "(" << p.x << ", " << p.y << ")";
    return o;
}

int main()
{
    Point p(1, 5);
    std::cout << p << std::endl;    // 出力: (1, 5)
    std::cout << ++p << std::endl;  // 出力: (2, 6)
    std::cout << p++ << std::endl;  // 出力: (2, 6)
    std::cout << p << std::endl;    // 出力: (3, 7)
    return 0;
}

前置と後置の区別は,引数の有無で区別されます。
後置インクリメント演算子の引数 (int) は,前置と区別するためのダミーの引数です。

この例からわかるように,一般に前置は後置よりも効率がよいと言えます。
C++ において i++ よりも ++i が多用されるのは,このためです。

スポンサーリンク