目黒@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と関連する良く見られている質問

Qstd::cout << p と std::cout << *p の違

std::cout << p と std::cout << *p の違いは?

VC++でコードを書きながらC++を学んでいます。
ポインタで疑問に思ったことがあるので質問させて下さい。

int* p;
int n = 100;
p = &a;

上記のように変数を宣言・代入した場合、
std::cout << p と std::cout << *p のどちらでも"100"と表示されます。
これら二つの違いは何でしょうか?
本来*pとすべきところをpにしてもVC++が気を利かせて
&pのアドレスに入っているデータを表示してくれているということでしょうか?

Aベストアンサー

No.1氏の回答にあるコードを元に答えます。

>この場合&pでもアドレスが表示されると思うのですが、
>「&p」と「p」の違いは何でしょうか?
pはaのアドレス。
&pはpのアドレス。
(「p = &a」では、どこのアドレスを渡しているのか考えてみて下さい)


「&p」と「p」の違いというのが参照とポインタの違いを聞いているのだとしたら、constのポインタ==参照型で良かったと思います。
p = &a; //OK
&a = p; //error
int* const p2 = p;
p2 = &a; //error

Qtry{}catch(){}とデストラクタの関係を教えてください。

try-catchでメモリ確保を含むクラスをスローした場合、デストラクタはどの時点で働くのか、教えてください。たとえば、↓の使いかたは大丈夫でしょうか?

【1】
try{
 throw(CError(100, "エラー情報"));
}catch(CError& err){
 //ここでerrを参照しても問題ないのでしょうか?
}

【2】
try{
 CError err(100, "エラー情報");
 throw(err); // (1)
}catch(CError& err){
 //ここでerrを参照しても問題ないのでしょうか?
 //まだデストラクタはちゃんと動作するのでしょうか?
 //catchが呼び出し元のメンバであったりしても大丈夫なのでしょうか?
}

宜しくお願いします。

Aベストアンサー

【1】【2】どちらの場合も問題がありません。
コンパイラが必要に応じてerrオブジェクトのコピーを作成します。
デストラクタが呼び出されるタイミングはコンパイラに依存するところもあると思いますが、
例えばVC7.1では【2】は以下のように動作します。
(1) errオブジェクトのコンストラクタが呼び出される
(2) CErrorクラスのテンポラリオブジェクト(以下a)のコピーコンストラクタが呼び出される。
(3) errオブジェクトのデストラクタが呼び出される
(4) catch文まで到達
(5) aオブジェクトのデストラクタが呼び出される。

VC7.1では、【1】は以下のように動作します。
(1) errオブジェクトのコンストラクタが呼び出される
(2) catch文まで到達
(3) errオブジェクトのデストラクタが呼び出される。

コンパイラがオブジェクトのコピーを省略しているようです。

Q{x = x>y ? x:y; return x;}

#include <iostream>
using namespace std;

inline int max(int x, int y){x = x>y ? x:y; return x;}

int main()
{
int num1, num2, ans;

cout << "2つの整数を入力して。\n";
cin >> num1 >> num2;

ans = max(num1, num2);

cout << "最大値は" << ans << "です。\n";

return 0;
}
の{x = x>y ? x:y; return x;}の部分の意味が解りません。

Aベストアンサー

inline int max(int x, int y){x = x>y ? x:y; return x;}
これを普通に関数で書くと

int max(int x, int y)
{
x = x>y ? x:y;
return x;
}

です。

x = 部分は右辺の結果が代入されます。これはわかりますよね。
x>y?x:y;
と書くと?より左にある条件式を判定し、その結果が真である場合は:で区切られた左側の値を、偽である場合は右の値を帰します。
x>yが真であればxを、偽であればyを返します。
それが、左辺値xに代入され、関数の戻り値として帰ります。

従って、2つの値をこの関数に入れると、大きいほうの値が帰ることになります。

QC++ : cout << (数字) で実行時エラーが発生する理由

ある特定の位置でcout を用いて数字を表示しようとするとエラーが発生してしまいます。

具体的には以下の関数内でのことなのですが、原因の分かる方がいましたら解答お願いします。

受け取った文字列を逆順にする関数です。



----------------------------------------------------------

void rev_str(char *a)
{
int length = 0;


cout << length; // ここでエラーが発生


while(true) {
if(*(a + length) == '\0') break;
length++;
}
for(int i = 0; i < length / 2; i++) {
char temp = a[i];
a[i] = a[length - i - 1];
a[length - i - 1] = temp;
}
}



---------------------------------------------------------


エラーの発生する部分ですが、数字ではなく文字・文字列なら問題なく表示されます。

このコードでは変数 length を表示しようとしていますが、length でなくても、またどのような『数』でも『この関数内』で cout を使用するとエラーが発生します。

この関数に何か間違いがあるのではないかと思うのですが、どうにも見つけることが出来ません。
コード全体はこの下に掲載します。

cout に何か制約があるのでしょうか。それとも、やはりコードのどこかに誤りがあるのでしょうか。わかる方がいましたら、解答お願いします。




全コード
------------------------------------------------------
#include <iostream>
#include <cstring>
using namespace std;

void rev_str(char *a);
void rev_str(const char *source, char *des);

int main()
{
char a[80], b[80];
strcpy(a, "hello, world!");
rev_str(a, b);
rev_str(a);
cout << a << "\n";
cout << b << "\n";

return 0;
}

void rev_str(char *a)
{
int length = 0;

cout << length;

while(true) {
if(*(a + length) == '\0') break;
length++;
}
for(int i = 0; i < length / 2; i++) {
char temp = a[i];
a[i] = a[length - i - 1];
a[length - i - 1] = temp;
}
}

void rev_str(const char *source, char *des)
{
char *a = (char*)malloc(sizeof(source));
strcpy(a, source);
rev_str(a);
strcpy(des, a);
free(a);
}

ある特定の位置でcout を用いて数字を表示しようとするとエラーが発生してしまいます。

具体的には以下の関数内でのことなのですが、原因の分かる方がいましたら解答お願いします。

受け取った文字列を逆順にする関数です。



----------------------------------------------------------

void rev_str(char *a)
{
int length = 0;


cout << length; // ここでエラーが発生


while(true) {
if(*(a + length) == '\0') break;
length++;
}
for...続きを読む

Aベストアンサー

> void rev_str(const char *source, char *des)
> {
>  char *a = (char*)malloc(sizeof(source));
>  strcpy(a, source);
>  rev_str(a);
>  strcpy(des, a);
>  free(a);
> }
の中でmallocで確保されるサイズは sizeof(source)なので
char型のポインタサイズ ... 32Bit環境なら4バイトってことになります
その領域に対して strcpyを実行するので4バイト以上の文字列をコピーすればメモリー領域が破壊されます

NULL終端の文字列が対象なら
char *a = (char*)malloc( strlen( source ) );
といった具合に変更してみましょう

Qchar AA[]{"全角文字"};から"全"という一字を取り出したい

 今晩は、Cの初心者です、宜しくお願いします。
 全角文字の入ったchar AA[]{"全角文字"};から"全"という文字一字を取り出す時にAA[0]とかくとエラーになります。
 どのようにしたら取り出せるのでしょう。
 ポインタを使う方法と使わない方法を教えて下さい。
 宜しくお願いします。

Aベストアンサー

お疲れ様です。

まずお伺いしたのがOSおよび開発するためのコンパイラです。

ロケール等の話は分かりませんが、昔のC言語で日本語を扱う場合には全角文字1文字で2個つのchar領域を使用していました。
(マルチバイト文字セットと言います。)

詳細は参考URLを参照の事。

windowsでVCと仮定した場合、charを使われていると言うことは、多分、shift-jis(シフトJIS)で文字列を扱っていると思われます。

結論として全角文字1文字だけを取り出したいという場合は、結局char2個分のデータを取り出す必要があります。

>char AA[]={'全','角'};

char AA[]="全角";
とし
>printf("%s%s\n" , AA[0],AA[1] ) ;

printf("%c%c\n" , AA[0],AA[1] ) ;
とすれば、「全」だけを表示する事が可能と思われます。

日本語を文字列で表示する為の文字コードについては
Shift-JISだけでなく、UnicodeやUTF・EUC・JISなどがあります。

もう少し詳しく記載してあるホームページはないか探してみましたが、ちょっと無理でした。

参考URL:http://marupeke296.com/CPP_charUnicodeWideChar.html

お疲れ様です。

まずお伺いしたのがOSおよび開発するためのコンパイラです。

ロケール等の話は分かりませんが、昔のC言語で日本語を扱う場合には全角文字1文字で2個つのchar領域を使用していました。
(マルチバイト文字セットと言います。)

詳細は参考URLを参照の事。

windowsでVCと仮定した場合、charを使われていると言うことは、多分、shift-jis(シフトJIS)で文字列を扱っていると思われます。

結論として全角文字1文字だけを取り出したいという場合は、結局char2個分のデータを取り出...続きを読む


人気Q&Aランキング

おすすめ情報