デストラクタ関数はコンストラクタ関数と対になる存在です。
デストラクタはオブジェクトが破棄された時、自動的に呼び出されます。

ローカルオブジェクトが破棄される時はオブジェクトを保持する関数がreturnした時です。
グローバルオブジェクトが破棄される時は、そのプログラムの実行が終了した時です。

デストラクタを宣言、定義する方法は、コンストラクタの書式に加え、関数の先頭に~を付けることです。
また、戻り値と引数を持たせることはできません。


このクラスから生成されたオブジェクトは、破棄される際にnameとHPをそれぞれヌル文字と0へ戻します。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
上の例はあまり実用的ではありません。ですが、コンストラクタで実行時メモリ確保を行い(malloc)デストラクタで実行時メモリ解放(free)を行うのは大変実用的です。
デストラクタの主な使い方はこれです。しかし、実行時メモリ確保は頻繁に使用するテクニックではない為、試したい方はもう一度mallocとfreeの使い方を復習して下さい。
【F限目】The Final Lesson※一番下です。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
デストラクタには副作用があります。厳密には副作用ではなく、正常な挙動なのですが、人間から見るとどうしても直感に反するというか、つまるところ都合の悪い挙動を起こすので副作用と呼ばれています。

ところでオブジェクトは変数のような存在とお話しました。
そのことを確認する為に、オブジェクトを引数として受け取り、オブジェクトを返却する関数を書いてみましょう。


PowerUP関数は、EnemyCharacterオブジェクトを受け取り、そのHPを+100して返却します。
EnemyCharacterクラスの下へ書いている点にご注目下さい。EnemyCharacterの上に書くと、コンパイラがEnemyCharacterの存在を把握せずにPowerUP関数を解読しようとする為、コンパイルエラーとなります。

では、PowerUP関数をmain関数から使用してみましょう。

156


Enemy1をPowerUP関数へ渡し、戻ってきたオブジェクトをそのままEnemy1へ代入しています。
これにより、魔王ラストボスのHPは100上昇しました。

さて、一見問題なく動いているように見えるこのコード、実はバグの温床があるのです。
それは……デストラクタ関数です。

想3:PowerUPへオブジェクトを渡した時、デストラクタは呼び出されるでしょうか?


デストラクタが2回呼び出されるとはどういうことでしょうか。
実は、関数へ変数やオブジェクトを渡す時、値渡しが行われます。
値渡しとは、渡す変数やオブジェクトと全く同じコピー変数あるいはコピーオブジェクトを作成し、関数内ではそれを操作する手法のことです。

つまり、PowerUP関数の終了時に、Enemy1のコピーオブジェクトのデストラクタが発動するのです。実際にはEnemy1はmain関数内でまだ使用しているにもかかわらず。

最も、コピーオブジェクトのデストラクタが発動しようが、main関数側のオブジェクトには関係ありません。
しかし、実はまだデストラクタは発動するのです。
なんと、C++のコンパイラは、変数やオブジェクトを戻す際にも、一時オブジェクトを作成するのです。これにより、戻り値としてオブジェクトを戻す際にも一時オブジェクトが作成され、すぐ破棄されるので、デストラクタが呼び出されます。
戻り値を戻す際になぜコピーが発生するかというと、戻り値の返却も値渡しだからです。まあこれもmain関数側のオブジェクトには関係ない一時オブジェクトなのでよいのですが。

まとめると、関数の終了時と、関数の返却時にコピーオブジェクトが生成され、関数の終了時と関数の返却時にコピーオブジェクトが破棄されるので、PowerUP関数へオブジェクトを渡した場合、デストラクタは意図せず(?)2回呼び出されるのです。

実際に確認してみましょう。
iostreamをEnemyCharacter.h側にもインクルードし、デストラクタの処理として文字列を画面へ出力します。

157