Menu
Coddy logo textTech

フレンド関数とクラス

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 32/104。

クラス外の関数やクラスが、そのクラスの非公開(private)メンバにアクセスする必要がある場合があります。それらのメンバを公開(public)にするのではなく、C++はカプセル化を維持しながら選択的なアクセスを許可するために friend キーワードを提供しています。

フレンド関数はクラスの内部で宣言されますが、そのクラスのメンバではありません。これはクラスのprivateおよびprotectedメンバにアクセスすることができます:

class Box {
    double width;
    
public:
    Box(double w) : width(w) {}
    
    // printWidthをフレンドとして宣言する
    friend void printWidth(const Box& b);
};

// フレンド関数の定義 - メンバ関数ではありません
void printWidth(const Box& b) {
    std::cout << b.width;    // プライベートメンバにアクセス可能
}

printWidth は、スコープ解決演算子なしでクラスの外で定義されていることに注目してください。これは、たまたま特別なアクセス権限を持っている通常の関数です。

クラス全体をフレンドに指定することもでき、その場合、そのクラスのすべてのメンバ関数が非公開メンバにアクセスできるようになります。

class Engine {
    int horsepower;
    
    friend class Mechanic;    // Mechanicはprivateメンバにアクセスできます
    
public:
    Engine(int hp) : horsepower(hp) {}
};

class Mechanic {
public:
    void diagnose(const Engine& e) {
        std::cout << e.horsepower;    // 許可されています - Mechanicはフレンドです
    }
};

フレンド関係は相互的なものではありません。もし EngineMechanic をフレンドとして宣言した場合、MechanicEngine の非公開メンバにアクセスできますが、その逆は不可能です。フレンドはクラス間に密結合を生じさせるため、控えめに使用してください。これらは演算子のオーバーロードや、密接に関連するヘルパー関数に最も役立ちます。

challenge icon

チャレンジ

簡単

MessageReaderクラスが、SecureMessageクラスに保存された暗号化されたメッセージを読み取るための特別なアクセス権を必要とする、安全なメッセージングシステムを構築しましょう。これは、フレンド関数とフレンドクラスを使用するのに最適なシナリオです。

コードを整理するために2つのファイルを作成します:

  • SecureMessage.h: プライベートなメッセージデータを保存し、信頼できるエンティティに選択的なアクセスを許可するSecureMessageクラスを定義します。クラスには以下を含める必要があります:
    • プライベートメンバ:content (string) と secretKey (int)
    • contentとsecret keyを受け取るコンストラクタ
    • プライベートメンバにアクセスできるように、MessageReaderをフレンドクラスとして宣言する
    • フレンド関数 decryptMessage(const SecureMessage& msg, int key) を宣言する。この関数は、提供されたキーがsecret keyと一致する場合のみcontentを返し、それ以外の場合は "Access Denied" を返します。

    次に、同じヘッダーファイル内で MessageReader クラスを定義します:

    • メッセージのプライベートな content に直接アクセスして返すメソッド readContent(const SecureMessage& msg)
    • 提供されたキーがメッセージの secretKey と一致するかどうかを確認し、"Valid" または "Invalid" を返すメソッド verifyKey(const SecureMessage& msg, int key)

    最後に、クラスの後に decryptMessage フレンド関数を定義します。

  • main.cpp: フレンド関数とフレンドクラスの両方が動作していることを示します。入力からメッセージの内容、シークレットキー、およびテストキーを(それぞれ別の行で)読み取り、以下の処理を行います:
    • contentとsecret keyを使用して SecureMessage を作成する
    • MessageReader オブジェクトを作成する
    • リーダーの readContent() メソッドを使用して、"Reader access: <content>" を出力する
    • テストキーを使用して verifyKey() を呼び出し、"Key verification: <result>" を出力する
    • テストキーを使用してフレンド関数を呼び出し、"Decrypt with test key: <result>" を出力する
    • 元のシークレットキーを使用してフレンド関数を呼び出し、"Decrypt with correct key: <result>" を出力する

フレンド関数は、スコープ解決演算子なしでクラスの外で定義されることに注意してください。これらは特別なアクセス権限を持つ通常の関数です。MessageReader クラスはフレンドであるため、そのすべてのメソッドで SecureMessage のプライベートメンバに直接アクセスできます。

std::stoi() を使用して、キーの入力を文字列から整数に変換してください。main.cpp#include "SecureMessage.h" を使用してヘッダーファイルをインクルードしてください。

チートシート

friendキーワードは、カプセル化を維持しながら、プライベートメンバへの選択的なアクセスを許可します。

フレンド関数: クラス内で宣言され、そのクラスのプライベートおよび保護されたメンバにアクセスできますが、クラスのメンバではない関数です。

class Box {
    double width;
    
public:
    Box(double w) : width(w) {}
    
    // フレンド関数の宣言
    friend void printWidth(const Box& b);
};

// スコープ解決演算子なしでクラス外で定義
void printWidth(const Box& b) {
    std::cout << b.width;    // プライベートメンバにアクセス可能
}

フレンドクラス: クラス全体をフレンドとして宣言することができ、そのすべてのメンバ関数にプライベートメンバへのアクセスを許可します。

class Engine {
    int horsepower;
    
    friend class Mechanic;    // すべてのMechanicメソッドがプライベートメンバにアクセス可能
    
public:
    Engine(int hp) : horsepower(hp) {}
};

class Mechanic {
public:
    void diagnose(const Engine& e) {
        std::cout << e.horsepower;    // 許可される
    }
};

重要なポイント:

  • フレンド関係は相互的ではありません。AがBをフレンドとして宣言した場合、BはAのプライベートメンバにアクセスできますが、その逆はできません。
  • フレンド関数は、スコープ解決演算子なしでクラス外で定義されます。
  • クラス間の密結合を生むため、フレンドの使用は控えめにしてください。
  • 演算子のオーバーロードや、密接に関連するヘルパー関数に最も役立ちます。

自分で試してみよう

#include <iostream>
#include <string>
#include "SecureMessage.h"
using namespace std;

int main() {
    // 入力を読み込む
    string messageContent;
    string secretKeyStr;
    string testKeyStr;
    
    getline(cin, messageContent);
    getline(cin, secretKeyStr);
    getline(cin, testKeyStr);
    
    int secretKey = stoi(secretKeyStr);
    int testKey = stoi(testKeyStr);
    
    // TODO: コンテンツと secret key を使用して SecureMessage を作成する
    
    // TODO: MessageReader オブジェクトを作成する
    
    // TODO: readContent() を使用して "Reader access: <content>" を出力する
    
    // TODO: test key で verifyKey() を使用して "Key verification: <result>" を出力する
    
    // TODO: test key で decryptMessage を使用して "Decrypt with test key: <result>" を出力する
    
    // TODO: secret key で decryptMessage を使用して "Decrypt with correct key: <result>" を出力する
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン