目黒@C++学習中 です。

二項演算子のオーバーライドする時
どうして"friend"にしないといけないの?

以下テスト ソース

#include <windows.h>

#pragma warning(push,3)
#include <iostream>
#include <iomanip>
#pragma warning(pop)

using namespace std;


class clsTest
{
private:
double m_dNum;
public:
clsTest() : m_dNum(0) {}
clsTest(double dNum) : m_dNum(dNum) {}
~clsTest(){};
friend clsTest operator + (const clsTest oP1,const clsTest oP2) {
return clsTest(oP1.m_dNum+oP2.m_dNum);
}
friend ostream& operator << (
ostream& oOutStream,const clsTest oT
) {
return oOutStream << "T(" << oT.m_dNum <<")";
}
};

void main()
{
clsTest oT1(2);
clsTest oT2(5);
clsTest oT3 = oT1 + oT2;

cout << setiosflags(ios::fixed) << setprecision(4);
cout << oT1 << endl;
cout << oT2 << endl;
cout << oT3 << endl;
}

このQ&Aに関連する最新のQ&A

C++ 学習」に関するQ&A: C言語の学習について

A 回答 (3件)

それは、あなたがやっているのが「メソッドのオーバーライド」ではなく、


「関数のオーバーロード」だからです。

クラスの宣言の中に実装を書いているので分かり難いですが、実装を
分離すると、

class clsText {
...
friend clsTest operator + (const clsTest oP1,const clsTest oP2);
friend ostream& operator << (ostream& oOutStream, const clsTest oT);
};

clsTest operator + (const clsTest oP1,const clsTest oP2)
{
  return clsTest(oP1.m_dNum+oP2.m_dNum);
}
ostream& operator << (ostream& oOutStream, const clsTest oT)
{
  return oOutStream << "T(" << oT.m_dNum <<")";
}

となります。両方ともグローバルなスコープを持つ + と << を
追加しているだけです。グローバルな関数の実装をしているのです
から、private なメンバを触るためには friend 宣言が必要になります。

operator+ は、メソッドのオーバーライドでも記述できます。


class clsText {
...
clsTest operator + (const clsTest oP2);
};

clsTest clsText::operator + (const clsTest oP2)
{
  return clsTest(m_dNum + oP2.m_dNum);
}

違いが分かりますか?

この回答への補足

では逆に Add関数をグローバルにする方法は?

class clsTest
{
private:
double m_dNum;
public:
clsTest() : m_dNum(0) {}
clsTest(double dNum) : m_dNum(dNum) {}
void Add(clsTest oT1,clsTest oT2) {
m_dNum = oT1.m_dNum + oT2.m_dNum;
}
// friend clsTest Add(clsTest oT1,clsTest oT2){}
};

void main()
{
clsTest oT1(2);
clsTest oT2(5);
clsTest oT3;

oT3.Add(oT1,oT2);
// oT3 = Add(oT1,oT2) こう書きたい
}

#クラスの基本的な理解が欠けているみたいで、すみません
#このへんのことが書いてある おすすめの本があれば教えて下さい

補足日時:2001/04/09 15:26
    • good
    • 0

? 根本的にわかってないような・・・C++の悪い記述の習慣を真似しているように見える・・・



friend など使う場面は比較的少ないです。

二項演算子オーバーライドについては、クラスの外に出してやる必要性があります。
これは例えば、文字列型とかで考えればわかりやすい。

string s;
s = "abc" + s;   --------(1)

クラス中のメソッドとして、演算子オーバーライドしてしまうと、(1)に対処させることができない。"abc"はchar* 型であり、 char*クラスにメソッドを付け加えることができない。だから、やむをえず、普通の関数オーバーライドと言う形をとるわけですね。


で。。。。

friend ostream& operator << (
ostream& oOutStream,const clsTest oT
) {
return oOutStream << "T(" << oT.m_dNum <<")";
}
};

このなかで、クラス内のプライベートメンバーに『強引に』アクセスしているから、friendが必要になってしまっている。
しかし、考えなければならない。本当にfriendは必要なのか?
もっといえば、double m_dNum の値を外部から取得されてはまずいのか?

こたえは否のはず。
double m_dNumというメンバーに直接触れられてはまずいが、m_dNumの値を外とやり取りすることは、例えば、setNum(cls Test&) や getNum()を定義することによって可能とするべきだろう。

すなわち、m_dNumの値は、クラスの外とやりとりしても良い。
m_dNumという『メンバーの名前』に触れられては困るが、『値』をやり取りされるのはOKなのだ。

そうなれば、

friend ostream& operator << (
ostream& oOutStream,const clsTest oT
) {
return oOutStream << "T(" << oT.m_dNum <<")";
}
};
このような定義の仕方は不適切であるとわかる。
おそらく、それは

ostream& operator <<(ostream& oOutStream, const& clsTest oT) {
return oOutStream << "T(" << oT.getNum() << ")";
}

という形にすることによって、2項演算子多重定義をクラスの内部の実装に触れさせることなく実現させることができる。

一方friendを使用するのは、
『メンバーの名前』の隠蔽性を犠牲にしてでも、いかなることがあっても『値』に触れさせてはまずいクラスフィールドに対して2項演算子オーバライド関数内でアクセスする場合である。

外部からその『値』をふれさせて良いかダメかは、set値 get値 といったメソッドを実装することが妥当かどうかで判断できるであろう。
    • good
    • 0

> では逆に Add関数をグローバルにする方法は?



class clsTest {
private:
  double m_dNum;
...
  friend clsTest Add(const clsTest& oT1, const clsTest& oT2);
};

void clsTest Add(const clsTest& oT1, const clsTest& oT2)
{
  return clsTest(oT1.m_dNum + oT2.m_dNum);
}

クラス宣言の中に実装を書いてしまうと、friend宣言が無いと、
メソッドとしてコンパイルされてしまいます。

friend宣言をしているのは、private なメンバーを参照して
いるからです。

また、関数 Add の引数が const な参照になっているのは、
必須ではありませんが、普通(*)はこう書きます。

  (*) 参照ではないと、無駄なインスタンスの複写が行われる

因みに、最初の回答のときには気がつきませんでしたが、
operator+ の引数も const な参照になっているべきです。
    • good
    • 0

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


人気Q&Aランキング