クラス class - seikei4 フレンド関数を使用したプログラムの例 #include #include ...
TRANSCRIPT
1
クラス Class(3)フレンド関数
上級プログラミング 講義資料
成蹊大学理工学部 情報科学科
2
あるクラスが自分のクラス以外の特定のクラスのメンバ関数のみに自分のprivateデータへのアクセスを許可しても良いケースが生じる。
このような場合、アクセスされるクラスの側で、その関数をfriend関数であると宣言しておくことによって、それが可能となる。
フレンド(friend)関数
3
フレンド関数クラス Girl
クラス Boy
クラス Other
friend Boy::print(Girl);
privateメンバ
name phone_num
print(Girl)
func() print()
アクセス可
アクセス不可
4
フレンド関数を使用したプログラムの例
#include <iostream>#include <string>using namespace std;class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため
// その前にGirlはクラスであることを明示しなければならない。class Boy{
string name;int age;
public:Boy(string x, int y){ name=x; age=y; }void print(Girl);
};class Girl{
string name;string phone_num;
public:Girl(string p, string q){ name=p; phone_num=q; }friend void Boy::print(Girl);
// Boyクラスのprint関数をフレンド関数であると宣言};
5
void Boy::print(Girl x){cout <<"Name : "<<name<<", Age : "<<age<<endl;cout <<"Her name: "<<x.name<<", TEL : "<<x.phone_num<<endl;
// print()はクラスGirlでfriend指定されているため、クラスGirlの// データメンバnameおよびphone_numにアクセスすることができる。
}int main(void){
Boy a("Bill",21);Girl b("Eluza","3210-0123");
a.print(b);}
実行例cserv1/tmp_mnt/home/usrX/ut994999/exp1% example5Name : Bill, Age : 21Her name: Eluza, TEL : 3210-0123cserv1/tmp_mnt/home/usrX/ut994999/exp1%
6
クラス自体のフレンド指定あるクラスのデータメンバに対して、他の特定のクラスの全メンバ関数からアクセスすることを許可したい場合には、そのクラス自体を以下のようにフレンド指定することで可能となる。
class Y; // 次のクラスXの中でクラスYを指名するので、// Xの前にYがクラスであることを宣言。
class X{friend class Y; // クラスYをクラスXのフレンドと指定int xdata;...
};class Y{
// クラスYの中からクラスXの全メンバにアクセス可能。// 逆はできない。
...};
7
演算子のオーバロード 演算子は関数の特殊ケース
例: +演算子 → operator+ という名の関数
二項演算子における2つの解釈obj1 + obj2
【解釈1】 operator+をobj1のクラスのメンバ関数と解釈
obj1.operator+(obj2) と等価
obj1:第1オペランド
obj2:第2オペランド
【解釈2】 両オペランドともoperator+の引数と解釈
operator+(obj1, obj2) と等価この場合、operator+はメンバ関数とはならない。
しかし、クラスのprivateメンバにアクセスする必要がある。→フレンド関数になる可能性
8
演算子のオーバロードの例 その1
int main(){
Complex x,y,z;x.input(); y.input(); z= x.add(y); z.print(); cout << endl;
}
int main(){
Complex x,y,z;
}
cin >> x >> y;
cout << z << endl;
z= x+y;
演算子を用いた、自然な記述
複素数クラス
9
cin(標準入力)は実行環境に1つなので、参照変数で受け渡す。複素数クラスComplexのメンバ関数ではない!
演算子の関数表現(1)
cin >> x >> y;
入力演算子 >>
operator>>(cin, x)
istream& operator>>( istream& , Complex& );
この関数のプロトタイプはどうなるか?
cinを受け取り、
cinを返す。
10
cin, coutの参照渡しについて
main
istreamクラスのオブジェクト
ostreamクラスのオブジェクト
cout<<y;
cin>>x;
11
cin, coutの参照渡しについて
main関数
cout<<y;
cin>>x;
operator>>
operator>>(istream inst, ・・・cinのコピーを受け取っても意味はない
?
12
cin, coutの参照渡しについて
main関数
cout<<y;
cin>>x;
operator>>
istream& operator>>(istream &inst, ・・・){・・・; return inst; }
operator>>関数はcin自体をinstという名前で使用する。そして最後に使用したinst、つまりcinを返す。
inst
13
演算子の関数表現(2)
cout << z << endl;
出力演算子 <<
operator<<(cout, z)
この関数のプロトタイプはどうなるか?
cout(標準出力)は実行環境に1つなので、参照変数で受け渡す。複素数クラスComplexのメンバ関数ではない!
ostream& operator<<( ostream& , Complex );
coutを受け取り、
coutを返す。
14
演算子の関数表現(3)
z = x + y ;
加算演算子 +
x.operator+(y)
この関数のプロトタイプはどうなるか?
複素数クラスComplexのメンバ関数として定義する
Complex operator+( Complex );
15
複素数クラスの設計の見直しclass Complex {
double real, imaginary; // 実数部realと虚数部imaginarypublic:
Complex(double a=0, double b=0){ real=a; imaginary=b; }Complex operator+(Complex k){
return Complex( real+k.real, imaginary+k.imaginary );}friend istream& operator>>(istream&, Complex&);friend ostream& operator<<(ostream&, Complex);
};
istream& operator>>(istream &inst, Complex &a){return inst >> a.real >> a.imaginary;
}ostream& operator<<(ostream &outst, Complex a){
outst << a.real; if(a.imaginary>0) outst << “+”;return outst << a.imaginary << “i”;
}
16
C++の数学ライブラリにある三角関数の引数は単位がradianである。これをdegree単位で扱うことができる新しいsinを作成する(関数のオーバロード)。加えてdegree単位の角度の加算や比較をする必要があるため、演算子のオーバロードも行う。
#include <iostream>#include <iomanip>#include <cmath>using namespace std;class Degree{
double deg;public:Degree(double x=0){ deg=x; }
// コンストラクタ以外はすべてフレンド関数friend double sin(Degree);friend Degree operator+(Degree, Degree);friend bool operator<=(Degree, Degree);friend ostream& operator<<(ostream& , Degree);
};
演算子のオーバロードの例 その2
17
double sin(Degree x){ // オリジナルのsin関数を呼び出して返すreturn( sin(x.deg*M_PI/180) );
}
Degree operator+(Degree a, Degree b){// mainのfor文中、n+10で使うため
return( Degree(a.deg+b.deg) );}
bool operator<=(Degree a, Degree b){// mainのfor文中、n<=180で使うため
return( (a.deg<=b.deg)? true:false );}
ostream& operator<<(ostream& out, Degree a){// mainの、cout<<nで使うため
return out << a.deg;}
続き
18
int main(void){Degree n;for(n=0; n<=180; n=n+10){
cout << "sin("<< resetiosflags(ios::showpoint)<< setiosflags(ios::fixed)<< setprecision(0) << setw(3) << n << ")=";
cout << setiosflags(ios::showpoint | ios::fixed)<< setprecision(10) << sin(n) << endl;
}return 0;
}
続き
19
再び複素数クラス・・・
main関数で次のようにするには、どうする?
Complex x(4,7), y(-2,3), z;
z=x+y; cout <<“z=“ << z << endl; // ①
z=x+2; cout <<“z=“ << z << endl; // ②
z=5+y; cout <<“z=“ << z << endl; // ③
① Complexのメンバ関数に Complex operator+(Complex)を定義② Complexのメンバ関数に Complex operator+(double)を定義③ Complex operator+(double, Complex) を外部関数として定義し、
Complexのフレンド関数に。
演算子のオペランドの組合せに合わせて、いくつものオーバロードを行なわなければならないのか?
20
合理的な方法(1)
① Complexのメンバ関数に Complex operator+(Complex)を定義② Complexのメンバ関数に Complex operator+(double)を定義
「①があれば、②は不要」②が無ければ x+2は
x.operator+(2) と解釈せざるを得ず、2はComplex型に自動キャストしなければならない。
x.operator+(Complex(2))つまり2はコンストラクタにより 2+0i となって、①のメンバ関数が使われる。
Complex x(4,7), y(-2,3), z;z=x+y; cout <<“z=“ << z << endl; // ①z=x+2; cout <<“z=“ << z << endl; // ②z=5+y; cout <<“z=“ << z << endl; // ③
21
合理的な方法(2)
③の第1引数doubleは、②のときと同様にComplexに自動キャストさせれば良い。すなわち 5+yは
operator+(Complex(5), y) と解釈させる。このようにするためには、
Complex operator+(Complex, Complex)という外部関数を定義し、Complexのフレンドにすれば良い。
その結果「③の関数があれば①の関数は不要」③だけですべてのオペランドの組合せに対応できることになる。
Complex x(4,7), y(-2,3), z;z=x+y; cout <<“z=“ << z << endl; // ①z=5+y; cout <<“z=“ << z << endl; // ③
① Complexのメンバ関数に Complex operator+(Complex)を定義③ Complex operator+(double, Complex) を外部関数として定義し、
Complexのフレンド関数に。
22
複素数クラスの設計の再見直しclass Complex {
double real, imaginary; // 実数部realと虚数部imaginarypublic:
Complex(double a=0, double b=0){ real=a; imaginary=b; }friend istream& operator>>(istream&, Complex&);friend ostream& operator<<(ostream&, Complex);friend Complex operator+(Complex, Complex);
};
istream& operator>>(istream &inst, Complex &a){return inst >> a.real >> a.imaginary;
}ostream& operator<<(ostream &outst, Complex a){
outst << a.real; if(a.imaginary>0) outst << “+”;return outst << a.imaginary << “i”;
}Complex operator+(Complex p, Complex q){
return Complex( p.real+q.real, p.imaginary+q.imaginary );}
23
メンバ関数か、フレンド関数か?
1. 作成するクラスのオブジェクトを扱う関数は、まずメンバ関数で考える。
2. 関数(または演算子)のオペランドの順序によりやむを得ない場合(入出力の取り扱いなど)や、同じ関数(または演算子)のオーバロードがうまく節約できるときには、フレンド関数を考える。
3. 無闇にフレンド関数を多用しないこと。フレンド関数は、所詮外部関数に過ぎない。本来のクラスの意味を忘れないこと。