キーワード friend は、特定の関数や他のクラスが、friendが宣言されたクラスのプライベートおよびプロテクトメンバーにアクセスできるようにします。friend関数はグローバルであったり、他のクラスのメソッドであったりすることができます。この構造により、クラスの内部状態にアクセスする必要がある関数を実装できますが、その関数は論理的にこのクラスのインターフェースとは関係ありません。
friendを使用することが推奨される場合:
注意:
例:
class Box { int width; public: Box(int w): width(w) {} friend void printWidth(const Box &b); }; void printWidth(const Box &b) { std::cout << b.width << std::endl; }
質問: friend関数は仮想関数にすることができますか?
よくある回答: はい、friendは関数の修飾子です。
正しい回答: いいえ、friend関数は仮想であることができません。なぜなら、彼らはクラスのメンバーではないからです!
例:
class Example { friend virtual void foo(); // コンパイルエラー: virtualはfriendに適用されません };
逸話: マトリックスライブラリの設計時に、すべての演算子をfriend関数にし、速度を向上させるために十分な状態を考慮せず、プライベートメンバーへのアクセスを不必要に開放しました。後に、プロジェクト内の他の関数がMatrixの内部状態を変更してしまう問題が発生しました。
逸話: コーポレートシステム内で、サポートクラスがプライベートメンバーへのアクセスを共有するためにfriendになりました。これにより循環依存が生じ、新しい機能の追加にはすべての関連クラスの変更が必要になりました。その後のリファクタリングには数週間かかりました。
逸話: プライベートテストのために、テストクラスをプロダクションクラスのfriendにすることにしました。ユニットテストのセットが複数出現した際に、実際に使用されるプライベートメソッドを追跡することが不可能になり、テストが内部実装に依存するようになり、コードの保守が難しくなりました。