関数とオブジェクト
オブジェクトの受け渡し
オブジェクト指向では、オブジェクトを関数に渡したり
あるいは関数の戻り値としてオブジェクトを返すことがあります
オブジェクトと関数の関係も、構造体のそれと同じです
プロトタイプで受け取るオブジェクトを指定することで、そのオブジェクトを受け取ります
C++では、C言語同様に関数の受け渡しはデフォルトで値渡しです
関数に渡されるオブジェクトはビット単位で物理的にコピーされます
したがって、関数内でオブジェクトの値を変更しても呼び出し側のオブジェクトには影響されません
当然、コピーされた関数側のオブジェクトは関数が終了すると同時に破棄されます
#include <iostream>
using namespace std;
class Kitty {
public:
char *str;
~Kitty();
}obj;
Kitty::~Kitty() {
cout << str << '\n';
}
void func(Kitty obj) {
obj.str = "Di Gi Gharat";
}
int main() {
obj.str = "Kitty on your lap";
func(obj);
return 0;
}
関数 func() では、渡された Kitty オブジェクトの str メンバ変数を更新しています
Kitty オブジェクトは破棄される時にデストラクタで str メンバ変数を標準出力に出力します
func()関数に渡されたオブジェクトは、main()関数のオブジェクトとは異なる存在です
func()関数内でオブジェクトのコピーのメンバ変数を変更しても、main()関数のオブジェクトに変化はありません
そして、func()関数の処理が終了するとコピーは破棄され Di Gi Gharat と言う文字が出力されます
その後、main()関数の処理も終了してmain()関数内のオブジェクトも破棄されます
このオブジェクトは初期値のままなので Kitty on your lap が出力されます
関数に渡す時にオブジェクトの複製を生成した時、コンストラクタは実行されません
オブジェクトは「複製」であればよいので、初期化処理の必要がないからです
しかし、上記したとおりデストラクタは実行されるという特徴があります
ここで実行されるデストラクタの数は、関数にオブジェクトを渡すコンパイラの実装方法で異なることがあります
オブジェクトのコピーを渡すという方法は、「オブジェクト」の章で説明したとおり予期せぬ副作用を起こすことがあります
その様な事態を避けたり、関数内でオブジェクトのメンバを更新したい場合
C言語同様に参照渡しを行います
関数はオブジェクトのポインタを受け取り、呼び出し側はオブジェクトのアドレスを渡します
#include <iostream>
using namespace std;
class Kitty {
public:
char *str;
~Kitty();
}obj;
Kitty::~Kitty() {
cout << str << '\n';
}
void func(Kitty *obj) {
obj->str = "Di Gi Gharat";
}
int main() {
obj.str = "Kitty on your lap";
func(&obj);
return 0;
}
上のプログラムを変更して、関数にオブジェクトのアドレスを渡すようにしました
オブジェクトのメンバへ間接参照し、内容を更新しています
今回は、関数に渡しているのがオブジェクトのアドレスのため
関数の処理が終了してもデストラクタは呼び出されないことがわかります
関数に渡されるのはオブジェクトのアドレスのため、ポインタからメンバ変数を更新することは
ポインタが指すオブジェクトのメンバ変数を更新することに同義です
オブジェクトを返す
関数にオブジェクトを渡すことができるということは
戻り値として受け取ることもできるということです
オブジェクトを返す時も、デフォルトで値渡しになります
関数はオブジェクトを返す時に複製を作成し、それを戻り値として返します
#include <iostream>
using namespace std;
class Kitty {
public:
char *str;
~Kitty();
};
Kitty::~Kitty() {
cout << "口からバスーカ ( ̄□ ̄)\n";
}
Kitty getKitty(char *str) {
Kitty obj;
obj.str = str;
return obj;
}
int main() {
Kitty obj = getKitty("Kitty on your lap\n");
cout << obj.str;
return 0;
}
getKitty() 関数は、内部で Kitty オブジェクトを生成し
戻り値として生成したオブジェクトの複製を返して終了します
関数の終了時にデストラクタが呼び出されますが
戻り値として得たオブジェクトは複製なので、破棄されることはありません
もし、戻り値としてポインタを返した場合は getKitty() が終了した時点で破棄されてしまいます
この場合、受け取った側のオブジェクトの動作は保証されないので注意が必要です
戻り値としてオブジェクトを渡すとき、オブジェクトにデストラクタ関数がある場合は注意が必要です
オブジェクトのメンバが動的にメモリを割り当てていた場合など
オリジナルも複製も同じメモリアドレスを指し、それがデストラクタで破棄される場合
いずれかのオブジェクトが破壊された時、同時にそのメンバのデータも保証されなくなります