クラス class - seikei4 フレンド関数を使用したプログラムの例 #include #include ...

23
1 クラス Class (3)フレンド関数 上級プログラミング 講義資料 成蹊大学理工学部 情報科学科

Upload: others

Post on 07-Jul-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

1

クラス Class(3)フレンド関数

上級プログラミング 講義資料

成蹊大学理工学部 情報科学科

Page 2: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

2

あるクラスが自分のクラス以外の特定のクラスのメンバ関数のみに自分のprivateデータへのアクセスを許可しても良いケースが生じる。

このような場合、アクセスされるクラスの側で、その関数をfriend関数であると宣言しておくことによって、それが可能となる。

フレンド(friend)関数

Page 3: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

3

フレンド関数クラス Girl

クラス Boy

クラス Other

friend Boy::print(Girl);

privateメンバ

name phone_num

print(Girl)

func() print()

アクセス可

アクセス不可

Page 4: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

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関数をフレンド関数であると宣言};

Page 5: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

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%

Page 6: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

6

クラス自体のフレンド指定あるクラスのデータメンバに対して、他の特定のクラスの全メンバ関数からアクセスすることを許可したい場合には、そのクラス自体を以下のようにフレンド指定することで可能となる。

class Y; // 次のクラスXの中でクラスYを指名するので、// Xの前にYがクラスであることを宣言。

class X{friend class Y; // クラスYをクラスXのフレンドと指定int xdata;...

};class Y{

// クラスYの中からクラスXの全メンバにアクセス可能。// 逆はできない。

...};

Page 7: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

7

演算子のオーバロード 演算子は関数の特殊ケース

例: +演算子 → operator+ という名の関数

二項演算子における2つの解釈obj1 + obj2

【解釈1】 operator+をobj1のクラスのメンバ関数と解釈

obj1.operator+(obj2) と等価

obj1:第1オペランド

obj2:第2オペランド

【解釈2】 両オペランドともoperator+の引数と解釈

operator+(obj1, obj2) と等価この場合、operator+はメンバ関数とはならない。

しかし、クラスのprivateメンバにアクセスする必要がある。→フレンド関数になる可能性

Page 8: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

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;

演算子を用いた、自然な記述

複素数クラス

Page 9: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

9

cin(標準入力)は実行環境に1つなので、参照変数で受け渡す。複素数クラスComplexのメンバ関数ではない!

演算子の関数表現(1)

cin >> x >> y;

入力演算子 >>

operator>>(cin, x)

istream& operator>>( istream& , Complex& );

この関数のプロトタイプはどうなるか?

cinを受け取り、

cinを返す。

Page 10: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

10

cin, coutの参照渡しについて

main

istreamクラスのオブジェクト

ostreamクラスのオブジェクト

cout<<y;

cin>>x;

Page 11: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

11

cin, coutの参照渡しについて

main関数

cout<<y;

cin>>x;

operator>>

operator>>(istream inst, ・・・cinのコピーを受け取っても意味はない

Page 12: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

12

cin, coutの参照渡しについて

main関数

cout<<y;

cin>>x;

operator>>

istream& operator>>(istream &inst, ・・・){・・・; return inst; }

operator>>関数はcin自体をinstという名前で使用する。そして最後に使用したinst、つまりcinを返す。

inst

Page 13: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

13

演算子の関数表現(2)

cout << z << endl;

出力演算子 <<

operator<<(cout, z)

この関数のプロトタイプはどうなるか?

cout(標準出力)は実行環境に1つなので、参照変数で受け渡す。複素数クラスComplexのメンバ関数ではない!

ostream& operator<<( ostream& , Complex );

coutを受け取り、

coutを返す。

Page 14: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

14

演算子の関数表現(3)

z = x + y ;

加算演算子 +

x.operator+(y)

この関数のプロトタイプはどうなるか?

複素数クラスComplexのメンバ関数として定義する

Complex operator+( Complex );

Page 15: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

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”;

}

Page 16: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

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

Page 17: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

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;}

続き

Page 18: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

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;

}

続き

Page 19: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

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のフレンド関数に。

演算子のオペランドの組合せに合わせて、いくつものオーバロードを行なわなければならないのか?

Page 20: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

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; // ③

Page 21: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

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のフレンド関数に。

Page 22: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

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 );}

Page 23: クラス Class - SEIKEI4 フレンド関数を使用したプログラムの例 #include  #include  using namespace std; class Girl; // Boyのメンバ関数print()の引数にGirlを指定しているため

23

メンバ関数か、フレンド関数か?

1. 作成するクラスのオブジェクトを扱う関数は、まずメンバ関数で考える。

2. 関数(または演算子)のオペランドの順序によりやむを得ない場合(入出力の取り扱いなど)や、同じ関数(または演算子)のオーバロードがうまく節約できるときには、フレンド関数を考える。

3. 無闇にフレンド関数を多用しないこと。フレンド関数は、所詮外部関数に過ぎない。本来のクラスの意味を忘れないこと。