授業で扱った複素数クラスのプログラムについて、以下のようなプログラムだと、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
}