
C ++でアプリケーションを設計する場合、クラスのプライベートメソッドへのアクセスを別のクラスまたはフリー関数に提供することが必要になる場合があります。 これを行うために、C ++にはfriendキーワードがあり、クラスのパブリックインターフェイスだけでなく、プライベートおよびすべての実装の詳細へのフルアクセスも提供します。 したがって、友人は「すべてまたは無」の原則に基づいて仕事をし、「すべて」は多すぎるかもしれません。 たとえば、Facadeクラスと複数のクライアントClient1、Client2がある場合、各クライアントに特定のメソッドセットへのアクセスのみを提供する必要があり、各クライアントは実装の詳細へのアクセスを提供せずに独自のセットを持つ必要があります。 C ++でこの問題を解決するには、すべての可能性があります。 この記事では、Attorney-ClientとPasskeyの2つのイディオムと、それらをゼロオーバーヘッドで使用する方法について説明します。
したがって、タスクは次のとおりです。サーバー、クライアント、および侵入者のクラスがあります。 クライアントはServer :: some_method()にアクセスする必要がありますが、実装の詳細にはアクセスできません。 同時に、侵入者はサーバーにアクセスできません。
class Server { private:
弁護士クライアント
弁護士と依頼人のイディオムはよりシンプルで簡単ですが、長いです-それから始めましょう。 クライアントに必要なアクセスを提供するには、単にサーバーの友達にすることはできません(サーバーのすべてのコンテンツへのアクセスを取得します)。また、必要なメソッドを公開することもできません(クラッカーもアクセスできます)。 この状況では、信頼できる仲介者が救助、またはむしろ弁護士になります。
class Attorney;
信頼チェーンは次のように編成されます。クライアントは弁護士の友人になり、それが別のサーバーになります。 Attorneyクラスには、サーバーへのリクエストをプロキシするプライベートインライン静的メソッドがあります。
class Server { private:
- lawyerクラスのプロキシメソッドはインラインである必要があり、その後、オプティマイザーはそれらを削除し、CardAccountクラスのメソッドを直接呼び出します。 コードをgodboltにコピーし、バリアント用に生成されたコードをproxy_some_method()および直接呼び出し(プライベートからパブリックに変更)と比較することで、確認するのは非常に簡単です。
- プライベートメソッドへのアクセスは、無料の機能で提供することもできます。 これを行うには、彼女を弁護士クラスの友人に任命する必要があります。
パスキー
プライベートインターフェイスへの選択的アクセスを提供する2番目の方法は、Passkeyイディオムです。 それはより短く、コードはよりきれいです。したがって、私はそれがより好きですが、もう少し明白ではありません。 タスクは同じです:サーバー、クライアント、侵入者、ただし今回はプロキシメソッドがパブリックとして宣言されていますが、プライベートコンストラクターを持つ特別なPasskeyパラメーターがそれらに追加され、明示的にリストされたフレンド(クラス、フリー関数)によってのみ呼び出すことができます。 Passkeyパラメータはユーティリティであり、プロキシ関数の呼び出し時にすぐに作成され、終了すると破棄されます(これは一時オブジェクトであり、変数に保存されません)。 その結果、void some_method(Passkey)は、Passkeyコンストラクターを呼び出すことができるクラスのみを呼び出すことができます(これらのクラスはすべて、Passkeyフレンドとしてリストされています)。
class Server { public: class Passkey { private: friend class Client;
読みやすさを向上させ、Passkeyクラスコードを他のクラスに含めることの重複を取り除くために、定型化して別のヘッダーファイルに入れることができます。
template <typename T> class Passkey { private: friend T; Passkey() noexcept {} Passkey( Passkey&& ) {} Passkey( const Passkey& ) = delete; Passkey& operator=( const Passkey& ) = delete; Passkey& operator=( Passkey&& ) = delete; };
Passkeyの唯一の目的は、一時インスタンスを作成してプロキシメソッドに渡すことです。このため、空のデフォルトコンストラクターと移動が必要です。他のすべてのコンストラクターと代入演算子は禁止されています(念のため、他の目的でPasskeyを使用しないため)。
呼び出し元クラス(クライアント、スーパークライアント)は、Passkeyパラメーターを構築できる各「独自の」パブリックメソッドのみを再び呼び出すことができます。 サーバーの実装の詳細は、「エイリアン」メソッドと同様に完全にアクセスできません。
- このバージョンでは、プロキシ関数もインラインで、呼び出しをさらにプロキシする必要があります。この場合(オプティマイザーが動作した後)、一時的なPasskey <>オブジェクトは作成されず、オーバーヘッドはゼロになります。
- パスキー<>をデフォルトの引数にすることはできません。 このオプションは機能しません:
class Server { public: void proxy_some_method( Passkey<Client> pass = Passkey<Client>() ); private: void some_method(); };
- わかりやすくするために、教育目的でのみproxy_プレフィックスを使用してプロキシメソッドを呼び出しました。
おわりに
説明されているAttorney-ClientおよびPasskeyのイディオムにより、クラスのプライベートメソッドへのアクセスを選択的に提供できます。 これらのメソッドは両方ともランタイムオーバーヘッドなしで機能しますが、追加のコードを記述する必要があり、friendキーワードを使用するよりもクラスインターフェースを目立たなくします。 プロジェクトでこの庭全体をフェンスで囲む必要があるか、それとも価値がないかは、あなた次第です。