複素数クラスのプログラム例への質問
授業で扱った複素数クラスのプログラムについて、以下のようなプログラムだと、a.add( b ) を実行すると、a の値が書き換わる。このため、次に a.mul( b ) を実行すると、(3+j5) * (2+j3) を実行する。もっと直感的な結果になるように、a の値が書き換わらないようにできないのか? といった趣旨の質問があった。
class Complex { private: double re , im ; public: Complex( double r , double i ) : re( r ) , im( i ) {} void print() { printf( "%f+j%f\n" , re , im ) ; } void add( Complex z ) { re = re + z.re ; im = im + z.im ; } void mul( Complex z ) { double r = re * z.re - im * z.im ; double i = re * z.im + im * z.re ; re = r ; im = i ; } } ; int main() { Complex a( 1 , 2 ) , b( 2 , 3 ) ; a.add( b ) ; // a = a + b ; a.print() ; // 3 + j5 a.mul( b ) ; // a = a * b ; ← aの値はすでに3+j5に変わった後 a.print() ; // (6-15) + j(10+9) = -9+j19 }
a の値が書き換わらないようにしたいのなら、以下のようなコードになるだろう。
対象オブジェクトを変化させない書き方
class Complex { : Complex add( Complex z ) { return Complex( re + z.re , im + z.im ) ; } Complex mul( Complex z ) { return Complex( re * z.re - im * z.im , // Complex オブジェクトを作って re * z.im + im * z.re ) ; // 返り値として返す。 } } ; int main() { Complex a( 1 , 2 ) , b( 2 , 3 ) ; a.add( b ).print() ; // 3+j5 a.mul( b ).print() ; // (2-6)+j(3+4) = -4+j7 }
ただ、このコードは、add() や mul() が Complex オブジェクトを作って返り値を返すが、その新しいオブジェクトはどのように呼び出し側に返されるのか?誰が廃棄するの? といった点で、単純なC言語の知識だけでは動作を理解しづらいことから、最初のコードにて説明を行った。でも、後者の方が計算結果のイメージは直感的だし、return コンストラクタ(…) の書き方に慣れてしまえば、プログラムも読みやすい!!
const メソッドとオブジェクトの参照渡し
前者のプログラムは、add() により 対象オブジェクトに副作用が発生する。後者は対象オブジェクトは変化しない。メソッドを呼び出す際にも、対象オブジェクトに副作用が発生しないことを明示したconstメソッドとして定義することで、オブジェクトを間違って破壊することから守ることもできる。
また、add() , mul() の引数は void add( Complex z ) {…} のような書き方では値渡しが行われる。つまり、メソッド呼び出し時点で実引数を仮引数にコピーする処理が発生する。このため、処理効率を考えるとポインタ渡し(参照渡し)の方がムダなコピーが発生しない。
一方で、参照渡しを行うと、ポインタを経由して引数に副作用を及ぼすことも可能となるため、参照渡しに const 宣言をつけることで、引数によるオブジェクト破壊を防ぐことができる。
class Complex { private: double re , im ; public: Complex( double r , double i ) : re( r ) , im( i ) {} void print() const { // 表示だけで副作用は発生しない printf( "%f+j%f\n" , re , im ) ; } Complex add( const Complex & z ) const { // 対象オブジェクトは変化しない // ~~~~~ ~~~ ~~~~~ // zに副作用なし 参照渡 constメソッド return Complex( re + z.re , im + z.im ) ; } Complex mul( const Complex & z ) const { return Complex( re * z.re - im * z.im , re * z.im + im * z.re ) ; } } ; int main() { Complex a( 1 , 2 ) , b( 2 , 3 ) ; a.add( b ).print() ; // 3+j5 a.mul( b ).print() ; // (2-6)+j(3+4) = -4+j7 }