◆ 引数丸々コピーはメッ! |
よく現場に出たての新人さんが犯す過ちの一つに、引数丸々コピーの術があります。 関数の引数に、クラス等のサイズの大きい引数を渡すとき、次のようにやっちゃうのです。 class AAA{ public: AAA(int a):m_a(a){] AAA(const AAA& cSrc){ m_a = cSrc.m_a; } int GetA() const { return m_a; } protected: int m_a; } bool IsSame(AAA cAAA1, AAA cAAA2){ return cAAA1.GetA() == cAAA2.GetA(); } void main() { AAA cOne(0), cTwo(1); if(IsSame(cOne, cTwo)){ printf("cOneとcTwoは同じ内容です。"); } else{ printf("cOneとcTwoは違う内容です。"); } }上の例では、IsSame関数内でcAAA1とcAAA2の内容は書き換えされませんね。 しかし実際は、次のように、IsSame関数に入るときに引数のインスタンスは丸々コピーされてしまうのです。 IsSame(cOne, cTwo) { AAA cAAA1(cOne); AAA cAAA2(cTwo); return cAAA1.GetA() == cAAA2.GetA(); }わかるでしょうか。 わざわざ別のインスタンスをつくって、比較だけさせるという無駄な処理が行われてしまいます。 しかも引数がクラスの場合は、メモリの無駄遣いになるだけでなく、クラスのコンストラクタが呼ばれるため、速度も低下してしまいます。 なので、こういう場合は次のように関数を定義しましょう。 bool IsSame(const AAA& cAAA1, const AAA& cAAA2){ return cAAA1.GetA() == cAAA2.GetA(); }参照を引数にすることで、インスタンスはコピーされることなく、そのまま関数にわたります。 参照渡しをすることで、関数内で値を変えられる危険性もでてきますが、引数にconstをつけることで、関数内では引数の内容を変更しませんよ、という証明(コンパイル時のチェック)をつけることができます。 もちろん上記のような例では速度もメモリも殆ど変わりませんが、1000件や10000件を超えるような繰り返し処理等を行う場合は、無駄なコピーやコンストラクタ呼び出しが致命的なボトルネックになる場合もあります。 習慣として、参照のみに必要な引数は参照渡しをするように心がけましょう。 |