私はC ++のFAQを読んでいて、そのfriend
宣言に興味がありました。個人的には使用したことがありませんが、言語を探求することに興味があります。
使用の良い例は何friend
ですか?
FAQをもう少し読むと、 <<
>>
演算子をオーバーロードして、それらのクラスの友達として追加です。しかし、これがカプセル化を壊さないかどうかはわかりません。これらの例外はいつOOPである厳密性の範囲内にとどまることができますか?
私はC ++のFAQを読んでいて、そのfriend
宣言に興味がありました。個人的には使用したことがありませんが、言語を探求することに興味があります。
使用の良い例は何friend
ですか?
FAQをもう少し読むと、 <<
>>
演算子をオーバーロードして、それらのクラスの友達として追加です。しかし、これがカプセル化を壊さないかどうかはわかりません。これらの例外はいつOOPである厳密性の範囲内にとどまることができますか?
回答:
まず(IMO)言う人の言うことを聞かない friend
役に立たない。それは便利です。多くの状況では、一般に公開することを意図していないデータまたは機能を持つオブジェクトがあります。これは特に、さまざまな領域に表面的にしか慣れていない多くの作成者がいる大規模なコードベースに当てはまります。
フレンド指定子に代わるものはありますが、それらはしばしば扱いにくい(cppレベルの具象クラス/マスクされたtypedef)か、絶対確実ではありません(コメントまたは関数名の規則)。
答えに。
friend
指定子は友人文を作り、クラス内の保護されたデータや機能に指定されたクラスのアクセスを可能にします。たとえば、次のコードでは、誰でも子供に名前を尋ねることができますが、名前を変更できるのは母親と子供だけです。
Windowなどのより複雑なクラスを検討することで、この単純な例をさらに進めることができます。おそらく、ウィンドウには多くの関数/データ要素があり、それらは公にアクセスできないようにする必要がありますが、WindowManagerなどの関連クラスで必要になります。
class Child
{
//Mother class members can access the private parts of class Child.
friend class Mother;
public:
string name( void );
protected:
void setName( string newName );
};
friend
強化することについて言及しています。と同様に、メンバーに選択的なアクセスをfriend
許可します。きめ細かな制御は、パブリックアクセスを許可するよりも優れています。他の言語も選択的アクセスメカニズムを定義しています。C#を検討してください。の使用に関するほとんどの否定的な批判は、より悪いカップリングに関連しています。ただし、場合によっては、より密な結合がまさに必要なものであり、その力を発揮します。protected
internal
friend
friend
friend
、やる気を起こさせる例を提供するよりも、何を説明することに重点が置かれているようです。Window / WindowManagerの例は、示されている例よりも優れていますが、あいまいです。この回答では、質問のカプセル化の部分についても触れていません。
職場では、コードのテストに友人を広範囲に使用しています。これは、メインアプリケーションコードに適切なカプセル化と情報非表示を提供できることを意味します。しかし、友人を使用して内部状態とテスト用のデータを検査する個別のテストコードを使用することもできます。
デザインの重要なコンポーネントとして、フレンドキーワードを使用しないと言うだけで十分です。
friend
キーワードは良い多くの用途があります。これは私にすぐに見える2つの使用法です。
フレンド定義ではクラススコープで関数を定義できますが、関数はメンバー関数としては定義されませんが、それを囲む名前空間のフリー関数として定義され、引数に依存するルックアップを除いて通常は表示されません。これにより、演算子のオーバーロードに特に役立ちます。
namespace utils {
class f {
private:
typedef int int_type;
int_type value;
public:
// let's assume it doesn't only need .value, but some
// internal stuff.
friend f operator+(f const& a, f const& b) {
// name resolution finds names in class-scope.
// int_type is visible here.
return f(a.value + b.value);
}
int getValue() const { return value; }
};
}
int main() {
utils::f a, b;
std::cout << (a + b).getValue(); // valid
}
場合によっては、ポリシーが派生クラスにアクセスする必要があることがわかります。
// possible policy used for flexible-class.
template<typename Derived>
struct Policy {
void doSomething() {
// casting this to Derived* requires us to see that we are a
// base-class of Derived.
some_type const& t = static_cast<Derived*>(this)->getSomething();
}
};
// note, derived privately
template<template<typename> class SomePolicy>
struct FlexibleClass : private SomePolicy<FlexibleClass> {
// we derive privately, so the base-class wouldn't notice that,
// (even though it's the base itself!), so we need a friend declaration
// to make the base a friend of us.
friend class SomePolicy<FlexibleClass>;
void doStuff() {
// calls doSomething of the policy
this->doSomething();
}
// will return useful information
some_type getSomething();
};
この回答には、そのための不自然な例があります。これを使用する別のコードはこの答えです。CRTPベースはこのポインターをキャストして、データメンバーポインターを使用して派生クラスのデータフィールドにアクセスできるようにします。
P<C>
にtemplate<template<typename> class P> class C : P<C> {};
「クラステンプレートCの使用はテンプレート引数が必要」述べました。あなたは同じ問題を抱えていたり、おそらく解決策を知っていますか?
FlexibleClass
内での使用は、FlexibleClass
暗黙的にそれ自身のタイプを参照する必要があります。
@roo:クラス自体がプライベートメンバーにアクセスできるユーザーを指示するため、カプセル化はここでは壊れていません。カプセル化は、これがクラスの外から引き起こされる可能性がある場合にのみ壊れます。たとえば、operator <<
「私はクラスの友達です」と宣言した場合などですfoo
。
friend
の使用でpublic
はなくの使用を置き換えますprivate
。
実際、C ++ FAQはすでにこれに答えています。
friend
も例外ではありません。ここでの唯一の実際の観察は、C ++がコンパイル時にのみカプセル化を保証することです。そして、あなたはそれを言うのにこれ以上言葉は必要ありません。残りはbollocksです。つまり、要約すると、FQAのこのセクションは言及する価値がありません。
正規の例は、operator <<をオーバーロードすることです。もう1つの一般的な用途は、ヘルパーまたは管理者クラスが内部にアクセスできるようにすることです。
C ++の友達について聞いたいくつかのガイドラインを次に示します。最後のものは特に印象的です。
friend
ことです。
編集:FAQをもう少し読むと、<< >>演算子のオーバーロードとそれらのクラスのフレンドとしての追加のアイデアが好きですが、カプセル化が壊れないかどうかはわかりません
それはカプセル化をどのように壊しますか?
データメンバーへの無制限のアクセスを許可すると、カプセル化が解除されます。次のクラスを検討してください。
class c1 {
public:
int x;
};
class c2 {
public:
int foo();
private:
int x;
};
class c3 {
friend int foo();
private:
int x;
};
c1
され、明らかにカプセル化されていません。誰でもそれを読んで変更できますx
。いかなる種類のアクセス制御を実施する方法もありません。
c2
明らかにカプセル化されています。への公開アクセスはありませんx
。できることは、foo
関数を呼び出すことだけです。この関数は、クラスに対して何らかの意味のある操作を実行します。
c3
?それはカプセル化されていませんか?への無制限のアクセスを許可しx
ますか?不明な機能へのアクセスを許可しますか?
いいえ。これにより、1つの関数でクラスのプライベートメンバーにアクセスできます。ちょうどしたようにc2
。と同様c2
に、アクセス権を持つ1つの関数は、「ランダムで不明な関数」ではなく、「クラス定義にリストされている関数」です。と同様にc2
、クラス定義を見るだけで、誰がアクセスできるかの完全なリストを確認できます。
それでは、これはカプセル化されていないのでしょうか?同じ量のコードがクラスのプライベートメンバーにアクセスできます。そして、アクセス権を持つすべての人がクラス定義にリストされます。
friend
カプセル化を壊しません。「OOP」と言うと、実際には「Java」を意味するため、一部のJavaの人々はプログラマーを不快に感じます。「カプセル化」と言っても、「プライベートメンバーを任意のアクセスから保護する必要がある」という意味ではなく、「プライベートメンバーにアクセスできる唯一の関数がクラスメンバーであるJavaクラス」です。いくつかの理由。
まず、すでに示したように、制限が厳しすぎます。friendメソッドに同じことを許可してはならない理由はありません。
第二に、それは十分に制限的ではありません。4番目のクラスを考えます。
class c4 {
public:
int getx();
void setx(int x);
private:
int x;
};
これは、前述のJavaの考え方によると、完全にカプセル化されています。 それでも、誰でもxを読んだり修正したりすることができます。それはどういう意味ですか?(ヒント:ありません)
結論:カプセル化とは、プライベートメンバーにアクセスできる関数を制御できることです。これらの関数の定義がどこにあるのかは正確ではありません。
Andrewの例のもう1つの一般的なバージョン、恐ろしいコードカップレット
parent.addChild(child);
child.setParent(parent);
両方の行が常に一緒に一貫した順序で行われるかどうかを心配する代わりに、メソッドをプライベートにして、整合性を強制するフレンド関数を持つことができます。
class Parent;
class Object {
private:
void setParent(Parent&);
friend void addChild(Parent& parent, Object& child);
};
class Parent : public Object {
private:
void addChild(Object& child);
friend void addChild(Parent& parent, Object& child);
};
void addChild(Parent& parent, Object& child) {
if( &parent == &child ){
wetPants();
}
parent.addChild(child);
child.setParent(parent);
}
言い換えると、パブリックインターフェイスを小さく保ち、フレンド関数のクラスとオブジェクトを横断する不変条件を適用できます。
addChild
メンバー関数にも親を設定させてみませんか?
setParent
管理することになるので、クライアントが親を変更できるようにしたくないので、より良い例は友達を作ることです。addChild
removeChild
メンバー/機能のアクセス権は、プライベート/保護/パブリック権を使用して制御しますか?したがって、これら3つのレベルのそれぞれの考えが明確であると仮定すると、何かが欠けていることは明らかです...
たとえば、保護されたメンバー/関数の宣言はかなり一般的です。この機能は誰もが手に届かないものです(もちろん、継承された子は除きます)。しかし、例外はどうですか?どのセキュリティシステムでも、何らかの「ホワイトリスト」を使用できますか?
したがって、フレンドは、堅固なオブジェクトの分離を柔軟に持つことができますが、正当化されると感じるものに対して「抜け穴」を作成することを可能にします。
なくても大丈夫なデザインは常にあるので必要ないという人もいると思います。私はそれをグローバル変数の議論に似ていると思います:それらを決して使うべきではありません、それらなしで行う方法は常にあります... ..これは友達と同じケースだと思います。
設定関数を使用せずにメンバー変数にアクセスできることを除いて、それは実際には何の効果もありません
まあそれはそれを正確に見る方法ではありません。アイデアは、WHOが何にアクセスできるかを制御することであり、設定機能の有無は、それとほとんど関係ありません。
friend
抜け穴はどうですか?これにより、クラスにリストされているメソッドがプライベートメンバーにアクセスできます。それでも、任意のコードがそれらにアクセスすることはできません。そのため、パブリックメンバー関数と同じです。
フレンドアクセスを使用する便利な場所を見つけました:プライベート関数のユニットテスト。
私が以前働いていた会社で、友人を使ってまともな影響を与えたところ、興味深い問題が発生しました。私はフレームワーク部門で働いて、カスタムOS上に基本的なエンジンレベルのシステムを作成しました。内部的にはクラス構造がありました:
Game
/ \
TwoPlayer SinglePlayer
これらのクラスはすべてフレームワークの一部であり、私たちのチームによって維持されていました。同社が作成したゲームは、ゲームの子供たちの1人から派生したこのフレームワークの上に構築されました。問題は、ゲームに、SinglePlayerとTwoPlayerがアクセスする必要があるさまざまなものへのインターフェイスがあり、フレームワーククラスの外部に公開したくないということでした。解決策は、これらのインターフェイスをプライベートにし、TwoPlayerおよびSinglePlayerが友情を介してそれらにアクセスできるようにすることでした。
正直なところ、この問題全体は、システムをより適切に実装することで解決できたはずですが、私たちは自分たちが持っていたものに縛られていました。
簡単に言えば、実際にカプセル化を改善するときに友人を使用します。読みやすさと使いやすさを向上させることも(演算子<<と>>が標準的な例です)、これも正当な理由です。
カプセル化を改善する例としては、他のクラスの内部で動作するように特別に設計されたクラス(テストクラスが思い浮かびます)が良い候補です。
<<
と>>
、通常はメンバーではなく友達になります。メンバーにすると、使いにくくなります。もちろん、私はそれらのオペレーターが個人データにアクセスする必要がある場合について話しています。そうでなければ、友情は役に立たない。
operator<<
とoperator>>
の代わりに、非メンバー、またはメンバーの値クラスのメンバーi|ostream
、希望の構文を提供しないであろう、と私はない、それを示唆しています。「これらのオペレーターがプライベートデータにアクセスする必要がある場合について話している」入出力オペレーターがプライベートメンバーにアクセスする必要がある理由がよくわかりません。
C ++の作成者は、それがカプセル化の原則を壊していないと言っています、そして私は彼を引用します:
「友達」はカプセル化に違反しますか? いいえ、違います。「友達」は、メンバーシップと同様に、アクセスを許可するための明示的なメカニズムです。(標準の適合プログラムでは)ソースを変更せずにクラスへのアクセスを許可することはできません。
はっきりしているだけではありません...
TDDを何度も行うために、C ++で 'friend'キーワードを使用しました。
友人は私についてすべてを知ることができますか?
更新:Bjarne Stroustrupのサイトの "friend"キーワードに関するこの貴重な回答を見つけました。
「友達」は、メンバーシップと同様に、アクセスを許可するための明示的なメカニズムです。
friend
キーワードをいつ、どこで使用するかについては十分に注意する必要があり、あなたのように、私はそれをほとんど使用していません。以下は、いくつかの使用上の注意friend
と代替案です。
2つのオブジェクトを比較して、それらが等しいかどうかを確認するとします。次のいずれかを実行できます。
最初のオプションの問題は、それが直接変数アクセスよりも(わずかに)遅く、読み取りが難しく、扱いにくい、多くのアクセサになる可能性があることです。2番目のアプローチの問題は、カプセル化を完全に解除することです。
すばらしいのは、クラスのプライベートメンバーにアクセスできる外部関数を定義できることです。これは次のfriend
キーワードで実行できます。
class Beer {
public:
friend bool equal(Beer a, Beer b);
private:
// ...
};
この方法は、equal(Beer, Beer)
今に直接アクセスしているa
とb
プライベートメンバーの(かもしれchar *brand
、float percentAlcohol
これはかなり不自然な例であるなど、あなたはすぐに適用されるfriend
、オーバーロードに== operator
、私たちはそれに取得します。
注意すべきいくつかの点:
friend
はクラスのメンバー関数ではありませんpublic
!)私は本当にfriends
他の方法でそれを行うのが非常に難しい場合にのみ使用します。別の例として、多くのベクトル数学関数はしばしばとして作成されているfriends
の相互運用性のためにMat2x2
、Mat3x3
、Mat4x4
、Vec2
、Vec3
、Vec4
、などそして、それは友達になるのではなく、どこにでもアクセサを使用する必要がありますするだけでそんなに簡単です。指摘しfriend
たように、<<
(デバッグに非常に便利です)、>>
そしておそらく==
演算子に適用すると便利ですが、次のようなものにも使用できます:
class Birds {
public:
friend Birds operator +(Birds, Birds);
private:
int numberInFlock;
};
Birds operator +(Birds b1, Birds b2) {
Birds temp;
temp.numberInFlock = b1.numberInFlock + b2.numberInFlock;
return temp;
}
私が言うように、私はまったくfriend
頻繁に使用しませんが、時々それはあなたが必要とするものだけです。お役に立てれば!
operator <<とoperator >>に関しては、これらの演算子を友達にする理由はありません。彼らがメンバー機能であってはならないのは事実ですが、彼らも友達である必要はありません。
最善の方法は、public print(ostream&)およびread(istream&)関数を作成することです。次に、これらの関数の観点から、operator <<およびoperator >>を記述します。これにより、仮想シリアル化を提供するこれらの関数を仮想化できるという追加の利点が得られます。
保護された関数を単体テストするために、フレンドキーワードのみを使用しています。保護された機能をテストすべきではないと言う人もいます。しかし、私は新しい機能を追加するときにこの非常に便利なツールを見つけました。
ただし、クラス宣言で直接キーワードを使用せず、代わりに気の利いたテンプレートハックを使用してこれを実現しています。
template<typename T>
class FriendIdentity {
public:
typedef T me;
};
/**
* A class to get access to protected stuff in unittests. Don't use
* directly, use friendMe() instead.
*/
template<class ToFriend, typename ParentClass>
class Friender: public ParentClass
{
public:
Friender() {}
virtual ~Friender() {}
private:
// MSVC != GCC
#ifdef _MSC_VER
friend ToFriend;
#else
friend class FriendIdentity<ToFriend>::me;
#endif
};
/**
* Gives access to protected variables/functions in unittests.
* Usage: <code>friendMe(this, someprotectedobject).someProtectedMethod();</code>
*/
template<typename Tester, typename ParentClass>
Friender<Tester, ParentClass> &
friendMe(Tester * me, ParentClass & instance)
{
return (Friender<Tester, ParentClass> &)(instance);
}
これにより、次のことが可能になります。
friendMe(this, someClassInstance).someProtectedFunction();
少なくともGCCとMSVCで動作します。
C ++では、 "friend"キーワードはオペレーターのオーバーロードとブリッジの作成に役立ちます。
1.)演算子のオーバーロードのFriendキーワード:演算子のオーバーロードの
例は次のとおりです。2つの浮動小数点変数
"x"(x座標用)と "y"(y座標用)を持つクラス "Point"があるとします。次に、"<<"
(抽出演算子)をオーバーロードして、"cout << pointobj"
とx座標とy座標が出力されるようにする必要があります(pointobjはクラスPointのオブジェクトです)。これを行うには、2つのオプションがあります。
1.「ostream」クラスの「operator <<()」関数をオーバーロードします。 2.「Point」クラスの「operator <<()」関数をオーバーロードします。ここで、最初のオプションは適切ではありません。別のクラスでこの演算子を再度オーバーロードする必要がある場合は、「ostream」クラスを再度変更する必要があるためです。
"operator <<()"
関数を呼び出すことができます
:1. ostreamオブジェクトcout.Asの使用:cout.operator <<(Pointobj)(フォームostreamクラス)。
2.オブジェクトなしで呼び出します。As:<<(cout、Pointobj)(Pointクラスから)。
Pointクラスにオーバーロードを実装したためです。したがって、オブジェクトなしでこの関数を"friend"
呼び出すには、オブジェクトなしでフレンド関数を呼び出すことができるため、キーワードを追加する必要があります。:今すぐ関数宣言はしたようになります
"friend ostream &operator<<(ostream &cout, Point &pointobj);"
ブリッジを作る際に2)友達のキーワード:
私たちは2つの以上のクラス(一般的に「ブリッジ」と呼ばれる)のアクセスプライベートメンバに我々が持っている機能を確認する必要がありますと仮定します。これを行う方法:
クラスのプライベートメンバーにアクセスするには、そのクラスのメンバーである必要があります。他のクラスのプライベートメンバーにアクセスするには、すべてのクラスでその関数をフレンド関数として宣言する必要があります。例:2つのクラスAとBがあるとします。関数"funcBridge()"
が両方のクラスのプライベートメンバーにアクセスする必要があります。次に、両方のクラスを次のように宣言する必要があり"funcBridge()"
ます。
friend return_type funcBridge(A &a_obj, B & b_obj);
これは友達のキーワードを理解するのに役立つと思います。
私が使用する特定のインスタンスの1つfriend
は、シングルトンクラスを作成するときです。friend
キーワードは、私は常にクラスの「でGetInstance()」メソッドを持つよりも簡潔でアクセサ関数を作成することができます。
/////////////////////////
// Header file
class MySingleton
{
private:
// Private c-tor for Singleton pattern
MySingleton() {}
friend MySingleton& GetMySingleton();
}
// Accessor function - less verbose than having a "GetInstance()"
// static function on the class
MySingleton& GetMySingleton();
/////////////////////////
// Implementation file
MySingleton& GetMySingleton()
{
static MySingleton theInstance;
return theInstance;
}
friend
がない場合、特定の「正当化」は必要ありません。
Friend関数とクラスは、クラスのプライベートおよび保護されたメンバーへの直接アクセスを提供して、一般的なケースでカプセル化を壊さないようにします。ほとんどの使用法はostreamでの使用です。次のように入力できるようにしたいと思います。
Point p;
cout << p;
ただし、これにはPointのプライベートデータへのアクセスが必要な場合があるため、オーバーロードされた演算子を定義します
friend ostream& operator<<(ostream& output, const Point& p);
ただし、カプセル化には明らかな影響があります。まず、フレンドクラスまたはフレンドクラスは、クラスのすべてのメンバーにフルアクセスできます。第2に、クラスとフレンドの実装は、クラスの内部変更によってフレンドが壊れる可能性のある点にまで統合されています。
友人をクラスの延長として見る場合、これは論理的に言えば問題ではありません。しかし、その場合、そもそもなぜ友達を槍で突く必要があったのでしょうか。
「友達」が達成しようとしているのと同じことを達成するために、カプセル化を壊すことなく、これを行うことができます:
class A
{
public:
void need_your_data(B & myBuddy)
{
myBuddy.take_this_name(name_);
}
private:
string name_;
};
class B
{
public:
void print_buddy_name(A & myBuddy)
{
myBuddy.need_your_data(*this);
}
void take_this_name(const string & name)
{
cout << name;
}
};
カプセル化は壊れていません。クラスBはAの内部実装にアクセスできませんが、結果はBをAのフレンドと宣言した場合と同じです。コンパイラーは関数呼び出しを最適化するため、これは同じ結果になります直接アクセスとしての指示。
「友達」を使用することは、議論の余地のあるメリットがあるが、確かなコストのショートカットであると思います。
これは実際のユースケースの状況ではないかもしれませんが、クラス間の友達の使用を説明するのに役立つかもしれません。
クラブハウス
class ClubHouse {
public:
friend class VIPMember; // VIP Members Have Full Access To Class
private:
unsigned nonMembers_;
unsigned paidMembers_;
unsigned vipMembers;
std::vector<Member> members_;
public:
ClubHouse() : nonMembers_(0), paidMembers_(0), vipMembers(0) {}
addMember( const Member& member ) { // ...code }
void updateMembership( unsigned memberID, Member::MembershipType type ) { // ...code }
Amenity getAmenity( unsigned memberID ) { // ...code }
protected:
void joinVIPEvent( unsigned memberID ) { // ...code }
}; // ClubHouse
メンバークラスの
class Member {
public:
enum MemberShipType {
NON_MEMBER_PAID_EVENT, // Single Event Paid (At Door)
PAID_MEMBERSHIP, // Monthly - Yearly Subscription
VIP_MEMBERSHIP, // Highest Possible Membership
}; // MemberShipType
protected:
MemberShipType type_;
unsigned id_;
Amenity amenity_;
public:
Member( unsigned id, MemberShipType type ) : id_(id), type_(type) {}
virtual ~Member(){}
unsigned getId() const { return id_; }
MemberShipType getType() const { return type_; }
virtual void getAmenityFromClubHouse() = 0
};
class NonMember : public Member {
public:
explicit NonMember( unsigned id ) : Member( id, MemberShipType::NON_MEMBER_PAID_EVENT ) {}
void getAmenityFromClubHouse() override {
Amenity = ClubHouse::getAmenity( this->id_ );
}
};
class PaidMember : public Member {
public:
explicit PaidMember( unsigned id ) : Member( id, MemberShipType::PAID_MEMBERSHIP ) {}
void getAmenityFromClubHouse() override {
Amenity = ClubHouse::getAmenity( this->id_ );
}
};
class VIPMember : public Member {
public:
friend class ClubHouse;
public:
explicit VIPMember( unsigned id ) : Member( id, MemberShipType::VIP_MEMBERSHIP ) {}
void getAmenityFromClubHouse() override {
Amenity = ClubHouse::getAmenity( this->id_ );
}
void attendVIPEvent() {
ClubHouse::joinVIPEvent( this->id );
}
};
アメニティ
class Amenity{};
ここでこれらのクラスの関係を見ると、クラブハウスには、さまざまな種類のメンバーシップとメンバーシップアクセスがあります。メンバーはすべて共通または共通のIDと列挙型を共有し、外部クラスが基本クラスにあるアクセス関数を介してそのIDとタイプにアクセスできるため、すべてスーパーまたは基本クラスから派生します。
ただし、Membersとその派生クラスのこの種の階層、およびClubHouseクラスとの関係を通じて、「特別な特権」を持つ派生クラスの1つだけがVIPMemberクラスです。基本クラスと他の2つの派生クラスはClubHouseのjoinVIPEvent()メソッドにアクセスできませんが、VIPメンバークラスは、そのイベントに完全にアクセスできるかのようにその特権を持っています。
VIPMemberとClubHouseを使用すると、他のメンバークラスが制限される双方向のアクセスストリートになります。
クラスのツリーアルゴリズムを実装するとき、教授が私たちに提供したフレームワークコードには、ノードクラスのフレンドとしてツリークラスがありました。
設定関数を使用せずにメンバー変数にアクセスできることを除いて、実際には何の効果もありません。
異なるクラス(互いに継承していないクラス)が他のクラスのプライベートまたは保護されたメンバーを使用している場合、友情を使用できます。
フレンド関数の典型的な使用例は、両方のプライベートまたは保護されたメンバーにアクセスする2つの異なるクラス間で実行される操作です。
http://www.cplusplus.com/doc/tutorial/inheritance/から。
非メンバーメソッドがクラスのプライベートメンバーにアクセスするこの例を見ることができます。このメソッドは、このクラス内でクラスのフレンドとして宣言する必要があります。
// friend functions
#include <iostream>
using namespace std;
class Rectangle {
int width, height;
public:
Rectangle() {}
Rectangle (int x, int y) : width(x), height(y) {}
int area() {return width * height;}
friend Rectangle duplicate (const Rectangle&);
};
Rectangle duplicate (const Rectangle& param)
{
Rectangle res;
res.width = param.width*2;
res.height = param.height*2;
return res;
}
int main () {
Rectangle foo;
Rectangle bar (2,3);
foo = duplicate (bar);
cout << foo.area() << '\n';
return 0;
}
あなたは可能性が最も厳しいと最も純粋なOOPの原則を遵守し、任意のクラスのためのデータメンバも持っていないことを確認アクセサをすべてのオブジェクトがなるようにしなければならない、間接を介して行われ、それらに基づいて行動するための唯一の方法とそのデータについて知ることができる唯一のものであれば、メッセージ、つまり、メソッド。
ただし、C#にも内部可視性キーワードがあり、Javaにはいくつかのデフォルトのパッケージレベルのアクセシビリティがあります。C ++は、他のどのクラスと他のクラスだけがそれを見ることができるかを正確に指定することにより、クラスへの可視性の妥協を最小限に抑えることにより、実際にはOOPの理想に近づきます。
私は実際にはC ++を使用していませんが、C#にフレンドがいる場合は、実際に多く使用するアセンブリグローバル内部修飾子の代わりに使用します。.NETでの展開の単位は、ので、それは本当に、incapsulationを破壊しないでアセンブリ。
ただし、クロスアセンブリフレンドメカニズムのように機能するInternalsVisibleTo Attribute(otherAssembly)があります。Microsoftはこれをビジュアルデザイナーアセンブリに使用します。
友達は、コールバックにも役立ちます。コールバックを静的メソッドとして実装できます
class MyFoo
{
private:
static void callback(void * data, void * clientData);
void localCallback();
...
};
where callback
はlocalCallback
内部的に呼び出し、にclientData
はインスタンスが含まれています。私の考えでは、
または...
class MyFoo
{
friend void callback(void * data, void * callData);
void localCallback();
}
これにより、友達はcppでcスタイルの関数として純粋に定義され、クラスを混乱させることがなくなります。
同様に、私がよく目にするパターンは、クラスのすべての本当にプライベートなメンバーを別のクラスに入れることです。これは、ヘッダーで宣言され、cppで定義され、Friendlyです。これにより、コーダーは、クラスの多くの複雑さと内部の作業をヘッダーのユーザーから隠すことができます。
ヘッダー:
class MyFooPrivate;
class MyFoo
{
friend class MyFooPrivate;
public:
MyFoo();
// Public stuff
private:
MyFooPrivate _private;
// Other private members as needed
};
cppでは、
class MyFooPrivate
{
public:
MyFoo *owner;
// Your complexity here
};
MyFoo::MyFoo()
{
this->_private->owner = this;
}
下流がこのように見る必要がないものを隠すことが容易になります。