一般的に難しいといわれているC++を紹介した記事です。
基本文法や変数、オブジェクトの概念を習得済みの中級者向けの記事です。
前回は、C++でのオブジェクトの取り扱いについて紹介しました。Javaとは異なり、代入は実体のコピーであるという内容です。
今回は、その代入(データのコピー)についてもう少し詳しく見て行きます。
1 2 3 4 5 6 7 8 9 10 11 12 |
[cpp] #include<stdio.h> using namespace std; class Myclass{ int value; public: void setValue(int val) { value = val; }; int getValue() { return value; }; void printValue(){ printf("%d\n",value); }; }; [/cpp] |
前回同様、上記のクラスを使って話を進めて行きます。
1 2 3 4 5 6 7 8 |
[cpp] int main(void){ Myclass m1; m1.setValue(100); Myclass m2 = m1; m2.printValue(); } [/cpp] |
このプログラムの実行結果は、画面に100と表示されます。イコール演算子による代入でm1の内容がm2にコピーされたからです。
それでは今回の本題ですが、イコール演算子を使用しない場合でも代入が行われる(代入と同じくオブジェクトがコピーされる)場合があります。それはどのような時でしょうか。
それは、関数(メソッド)を使ってデータをやり取りした場合です。
オブジェクトを使わない、単純なデータ型でのサンプルコードで見て行きましょう。
1 2 3 4 5 6 7 8 9 10 11 |
[cpp] int func(int arg){ arg += 100; // arg = arg + 100;と同義 int ret = arg; return ret; } int main(void){ int val = 10; int ans = func(val); } [/cpp] |
func()関数はint型を受け取り、int型を返す関数です。関数内で変数argに100を加算していますが、変数valには影響しません。
このとき、変数valから変数argへ、変数retから変数ansへ、関数内では変数argから変数retへ、データが受け渡されています。特に意識していなかった人も居るかもしれませんが、この引数および戻り値のやり取りもデータのコピーが行われているのです。
それでは、オブジェクトを使った関数の例を見て行きましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[cpp] Myclass func(Myclassarg){ arg.setValue( arg.getValue() + 100 ); Myclass ret = arg; return ret; } int main(void){ Myclass val; val.setValue(10); Myclass ans = func(val); ans.printValue(); } [/cpp] |
このときも、変数valから変数argへ、変数retから変数ansへ、関数内では変数argから変数retへ、データが受け渡されており、それぞれの場所でデータのコピーが行われています。
今回、Myclassはサンプルとしてのクラスなので、int型変数を一つと、それに関する関数を三つ持っているだけですが、実際には多くの変数や関数を含むクラスになります。
そのときに、関数に渡すたびにコピーをしていてはメモリの無駄ですし、コピーをする処理の時間も無駄です。
Javaではこのような書き方をしたとき、オブジェクトの参照情報のコピーを渡すので、実際にオブジェクトに含まれるデータのコピーは作成されません。C++ではデータのコピーを作らず、そのデータの場所の情報を受け渡すときはポインタを使います。(参照渡しというのもありますが、それは別の機会に。)
下記のサンプルはデータのコピーを作らない、ポインタでのデータのやり取りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[cpp] Myclass *func(Myclass*arg){ arg->setValue( arg->getValue() + 100 ); Myclass *ret = arg; return ret; } int main(void){ Myclass val; val.setValue(10); Myclass *ans = func(&val); ans->printValue(); } [/cpp] |
表示結果は一つ前のサンプルと同じですが、データをコピーしていない点で異なります。
main()関数の最後の行を val.printValue(); としても同様の結果になります。
Javaではオブジェクトの受け渡しは参照型のみで行われますが、C++では標準的には実体のコピーで行われ、ポインタを使うことでJavaの参照情報のやりとりと同様のことができるようになります。