C,C++分科会テキスト06
目的
複素数の計算をプログラミングしたい。
z1=x1+iy1
z2=x2+iy2
だったとする。
このとき、z1+z2をあらわすとき、成分を使って
double x1,y1;
double x2,y2;
double x3,y3;
x3=x1+x2;
y3=y1+y2;
などとはあらわしたくない。
z1+z2なのだから、
complex z1,z2,z3;
z3=z1+z2;
としたいと思うのが普通であろう。
そんな夢が「構造体」と「演算子オーバーロード」で可能になります。
目次
- 構造体とは
- 構造体は複数の別の型が集まったものです。
- 構造体と関数
- 構造体はほかの型のように関数の引数や、戻り値に設定できます。
- 構造体と演算子オーバーロード
- お好みの構造体をほかの型のように自由に使うために、+、−などの演算も定義します。
- 課題
- 複素数型の演算を作りましょう
構造体は複数の別の型が集まった型です。どのように宣言するかというと、
struct complex{
double x; //real part
double y; //imaginary part
};
です。これでcomplexという型を作れました。このように宣言しておくと、
struct complex z;
とすれば、zはxとyの成分を持った「複素数型」の変数として扱えます。
また、この代わりに
struct complex z={1,2};
とすれば、zが宣言されると同時にzはxが1、yが2に初期化されます。
実はC++では変数を宣言するときにいちいちstructをくっつける必要はありません。
complex z={1,2};
でよいのです。(Cだとtypedefを使わなくてはなりません。)
以下はcomplexがこのように定義されているとして考えています。
さて、構造体を作るとその成分も変数として作られます。
この変数は、それぞれz.x、z.yという名前であり、
ほかの変数とまったく違いはありません。
complex z;
double y;
y=z.y; //値を取り出すことができる。
z.x=2.718281828;
z.y=1.701; //値を入れることができる
まったくほかの方の変数と同じように構造体へのポインタもOkです。
complex z;
complex *pz;
pz=&z;
という具合です。このとき「(*pz).x」すなわち「z.x」は「pz->x」とも表現します。
構造体はを関数の引数として使ったり、戻り値として使えます。
そこで複素数を足し合わせる関数を考ます。
inline complex plus(complex z1, complex z2)
{
complex z3={
z1.x+z2.x,
z1.y+z2.y,//このコンマはあっても無くても良い。
};
return z3;
}
すると、
complex hoge1,hoge2,hoge3;
hoge3=plus(hoge1,hoge2);
と簡単にできます。
「inline」というのは、関数が呼ばれるところに関数を貼り付けてしまう方式を示しており、関数呼び出しの手間を省くことができます。
あっても無くても関数の作用は変わりません。
さてしかし足し算をするのにいちいちこんな関数を呼んでいたのでは、
plus(plus(plus(a,b),minus(c,plus(d,e))),f)
というわけのわからないことになります。
そこで今回ご紹介するのは…
これはCにないC++の機能です。
plus(plus(plus(a,b),minus(c,plus(d,e))),f) 使用前
↓
a+b+(c-(d+e))+f 使用後
要するに構造体で作った新しい型に演算子を使えるようにしてやろうというアイディアです。
実際すでに「=」という代入を示す演算子はちゃんと使えています。
どうするかというと非常に簡単です。
演算子と関数は同じようなものなので、演算子の作用を関数で書き直せるからです。
具体的には上で示した「plus」を「operator+」にかえるだけでよいのです。
inline complex operator+(complex z1,complex z2)
{
complex z_return={
z1.x + z2.x,
z1.y + z2.y,
};
return z_return;
}
int main(){
complex hoge1,hoge2,hoge3;
hoge3=hoge1+hoge2;
return 0;
}
出来上がりました。
1.複素数型の掛け算をつくってください。
2.定数倍はどうすればよいでしょう。
3.「+=」を作ってみましょう。
答え
1.
inline complex operator*(complex z1,complex z2)
{
complex z_return={
z1.x*z2.x-z1.y*z2.y,
z1.x*z2.y+z1.y*z2.x,
};
return z_return;
}
2.
inline complex operator*(double a,complex z2)
{
complex z_return={
a*z2.x,
a*z2.y,
};
return z_return;
}
3.
inline complex& operator+=(complex& z1,complex z2)
{
z1.x+=z2.x;
z1.y+=z2.y;
return z1;
}
これは上の二つとは違うつくりである。まず complex&…であるがこれは、complex型の「変数」自身を示すための参照型である。(04を参考)
これに対し今までのcomplexはcomplex型の「値」を示していた。
今までの引数でのcomplex z1というのはcomplex型の値でz1を初期化して使うという意味だったため
z1自身を操作して呼び出したほうの変数の値には影響が無かった。
しかしこのcomplex& z1は値ではなく変数自身をz1に入れる。z1とその変数がまったく同じ物として扱われることとなる。
それゆえに、z1をいじると、呼び出したほうも変化してくれるのである。