C++のフレンド演算子について詳しく解説

C++には、クラスの外部からでもそのクラスの非公開メンバーにアクセスすることができる「フレンド演算子」という機能があります。

この記事では、フレンド演算子の基本的な使い方や注意点をわかりやすく解説します。

目次

C++のフレンド演算子とは

C++には、クラスの外部からでもそのクラスの非公開メンバーにアクセスすることができる「フレンド関数」という機能があります。このフレンド関数をさらに拡張したものが「フレンド演算子」です。

フレンド演算子は、あるクラスの非公開メンバーに対して、他のクラスや関数から直接アクセスするための演算子です。

通常、演算子はオブジェクト同士を操作するために使用されますが、フレンド演算子はオブジェクトと非公開メンバーを操作するために使用されます。

例えば、以下のようなコードがあった場合、operator+Complex クラスの非公開メンバー real_imag_ に直接アクセスできるようにすることができます。

class Complex {
private:
    double real_;
    double imag_;
public:
    Complex(double r, double i) : real_(r), imag_(i) {}
    friend Complex operator+(const Complex& lhs, const Complex& rhs);
};

Complex operator+(const Complex& lhs, const Complex& rhs) {
    return Complex(lhs.real_ + rhs.real_, lhs.imag_ + rhs.imag_);
}

このように、フレンド演算子を使うことで、プログラム全体で共有される複雑な処理を簡単かつ効率的に実装することができます。

フレンド演算子の使い方

C++において、フレンド演算子はクラスの外部からでもそのクラスの非公開メンバーにアクセスすることができるようにするための機能です。

フレンド演算子の定義方法

フレンド演算子を定義するには、以下のような形式で宣言します。

friend 戻り値型 operator 演算子シンボル(引数リスト);

例えば、以下のようなコードで「+」演算子をフレンド演算子として定義し、クラス外で実装することができます。

class MyClass {
private:
    int value;
public:
    MyClass(int v) : value(v) {}
    friend MyClass operator+(const MyClass& lhs, const MyClass& rhs);
};

MyClass operator+(const MyClass& lhs, const MyClass& rhs) {
    return MyClass(lhs.value + rhs.value);
}

フレンド演算子の呼び出し方法

フレンド演算子は通常のメソッドと同じように呼び出すことができます。例えば、上記で定義した「+」演算子を使用する場合は以下のように書くことができます。

MyClass a(1), b(2), c = a + b;

このように、フレンド演算子を使用することで、クラス内部だけではなく外部からも非公開メンバーにアクセスすることが可能になります。ただし、過剰な使用はプログラム全体を複雑化させるため注意が必要です。

フレンド演算子のメリット

プライベートメンバーへのアクセス

フレンド演算子を使用することで、通常はアクセスできないプライベートメンバーにアクセスすることができます。これは、クラス内部で定義された関数や外部の関数からも同様に利用可能です。

例えば、以下のようなコードでは、MyClass クラスの privatex メンバー変数にアクセスするために、フレンド演算子を使用しています。

class MyClass {
private:
    int x;
public:
    friend void myFunction(MyClass&);
};

void myFunction(MyClass& obj) {
    obj.x = 10; // MyClass の private 変数 x にアクセス
}

クラスのカプセル化の維持

フレンド演算子を使用することで、クラス内部のデータや振る舞いを隠蔽し、カプセル化を維持することができます。つまり、外部から直接的にアクセスされることがなくなります。

例えば、以下のようなコードでは、MyClass クラス内部で定義された privateMethod() 関数をフレンド演算子を使用して呼び出すことができます。

これにより、外部から直接的に privateMethod() 関数にアクセスされることが防止されます。

class MyClass {
private:
    void privateMethod() {
        // do something...
    }
public:
    friend void myFunction(MyClass&);
};

void myFunction(MyClass& obj) {
    obj.privateMethod(); // MyClass の private 関数 privateMethod() にアクセス
}

以上がフレンド演算子を使用するメリットです。ただし、過剰な使用はカプセル化を崩壊させる可能性があるため注意が必要です。

フレンド演算子の例

フレンド演算子を使った演算の例

フレンド演算子は、クラス外部からでもプライベートメンバーにアクセスすることができるため、便利な機能です。以下は、フレンド演算子を使った加算処理の例です。

#include <iostream>
using namespace std;

class MyClass {
    private:
        int num;
    public:
        MyClass(int n) : num(n) {}
        friend MyClass operator+(const MyClass& a, const MyClass& b);
        void show() { cout << "num = " << num << endl; }
};

MyClass operator+(const MyClass& a, const MyClass& b) {
    return MyClass(a.num + b.num);
}

int main() {
    MyClass obj1(10), obj2(20), obj3(0);
    obj3 = obj1 + obj2;
    obj3.show();
    return 0;
}

上記のコードでは、MyClassクラス内でoperator+関数をフレンド関数として定義しています。

この関数は、2つのMyClassオブジェクトを受け取り、それらのnumメンバ変数を加算した結果を新しいオブジェクトとして返します。

そして、この関数で返されたオブジェクトを代入することで、2つのオブジェクトの和を計算しています。

フレンド演算子を使った入出力の例

次に、フレンド演算子を使った入出力処理の例です。

以下は、文字列型データメンバーを持つMyStringクラスに対して、ストリーム挿入子(<<)およびストリーム抽出子(>>)をフレンド演算子として定義し、文字列型データメンバーへ直接アクセスする方法です。

#include <iostream>
#include <string>
using namespace std;

class MyString {
    private:
        string str;
    public:
        MyString(string s) : str(s) {}
        friend ostream& operator<<(ostream& os, const MyString& ms);
        friend istream& operator>>(istream& is, MyString& ms);
};

ostream& operator<<(ostream& os, const MyString& ms) {
    os << ms.str;
    return os;
}

istream& operator>>(istream& is, MyString& ms) {
    is >> ms.str;
    return is;
}

int main() {
    MyString str("Hello World!");
    
    // ストリーム挿入
    cout << "str: " << str << endl;

    // ストリーム抽出
    cout << "Input string: ";
    cin >> str;
    
    cout << "str: " << str << endl;

   return 0;
}

上記のコードでは、MyStringクラス内でストリーム挿入子(<<)およびストリーム抽出子(>>)関数をフレンド関数として定義しています。これにより、直接文字列型データメンバーにアクセスすることが可能になります。また、上記コードではストリーム挿入・抽出処理が行われています。

目次