お前のものは俺のもの。俺のものも俺のもの。今回はそういうお話です。
では、今回の要点です。
では、いってみましょう。
あるクラスにとても密接に関係する関数やクラスを作ったとします。こういう場合、どうしても protected メンバや private メンバにアクセスしたくなる場合が出てくることがあります。なるべくこのようなことはやりたくはないのですが、どうしようもないことがあります。
例えば、演算子のオーバーロードを考えてみましょう。分数を扱うクラス fraction を例に考えます。
class fraction { private: int m_nNumer; // 分子 (numerator) int m_nDenom; // 分母 (denominator) public: fraction(int nNumer = 0, int nDenom = 1) { m_nNumer = nNumer; m_nDenom = nDenom; } operator int() { return m_nNumer / m_nDenom; } }; |
複雑になると分かりにくいので、かなり手抜きです。値は、割った結果を int で取得することしかできません。
では、int から fraction を引くことを考えましょう。ここで注意することは fraction から int を引くのではないというところです。fraction から int を引くのなら
fraction fraction::operator-(int nSub) { fraction ret(m_nNumer - m_nDenom * nSub, m_nDenom); return ret; } |
で構いません。この逆を行うには、
fraction operator-(int nNum, fraction frcSub);
といった形のグローバル関数を使う必要があります。左項がクラスでない場合は、こういう風にする必要があります。
では作ってみましょう。
fraction operator-(int nNum, fraction frcSub) { fraction ret(frcSub.m_nDenom * nNum - frcSub.m_nNumer, frcSub.m_nDenom); return ret; } |
...と、こうしたいわけですが、この関数は fraction のメンバ関数ではないので private メンバである m_nNumer と m_nDenom は使えません。このクラスなら m_nNumer や m_nDenom を公開する関数を作っても別に構わないのですが、そうしてしまうと話が進まないのでやりません(汗)。(いい例が思いつかなかったもので...。)
ではどうするかというと、この関数でも private メンバにアクセスできるようにします。どうやればいいのかというと、この関数とお友達になってしまえばいいのです。友達になら、少々プライベートなことも教えてくれるというわけです。
で、「お友達になる」には「今日からキミも友達だ!」と宣言する必要があります。その命令が friend です。まんまですね。こんな風に使います。
class fraction { private: int m_nNumer; // 分子 (numerator) int m_nDenom; // 分母 (denominator) public: fraction(int nNumer = 0, int nDenom = 1) { m_nNumer = nNumer; m_nDenom = nDenom; } operator int() { return m_nNumer / m_nDenom; } friend fraction operator-(int nNum, fraction frcSub); }; |
赤で書かれた部分が追加された部分です。このように書くと、friend の次に書かれた関数の中から fraction の protected, private メンバを使うことができるようになります。この機構のことをそのままフレンドと呼びます。そして、フレンドに指定された関数のことをフレンド関数と呼びます。
これで無事引き算ができるようになりました。
フレンドに指定できるのは関数だけではありません。クラスも指定することができます。フレンドに指定されたクラスは、やはりフレンドクラスと呼びます。フレンドクラスからは、やはり指定元のクラスの protected, private メンバを使うことができます。
しかし、フレンドクラスの protected, private メンバを、指定元のクラスから使うことはできません。C++の友情は一方通行なのです。そういう風にしたければ、相互にフレンド指定を行う必要があるのです。
また、フレンドに指定された関数やクラスは、別に宣言、実装を行う必要があります。friend 文はあくまでフレンドの指定を行うための文であり、プロトタイプ宣言などにはならないのです。
そして、friend 文の前に指定する関数やクラスの宣言がなくても構いません。後に書いたのでもいいということです。このあたりはC++にしては異質の仕様に思われるかも知れませんね。クラスを指定するときは friend class CTest; のように、class も書く必要があります。
但し、メンバ関数をフレンド指定するときだけは例外的に先に宣言が必要です。注意して下さい。
では、今回の要点です。
フレンドは強力ですが、使いどころを間違えるとアクセス指定の意味を無くしてしまうことになります。十分注意して使うようにしましょう。
それでは、次回まで。
Last update was done on 2001.2.23
この講座の著作権はロベールが保有しています