例えば:
int main() {
Foo *leedle = new Foo();
return 0;
}
class Foo {
private:
somePointer* bar;
public:
Foo();
~Foo();
};
Foo::~Foo() {
delete bar;
}
デストラクタはコンパイラによって暗黙的に呼び出されますか、それともメモリリークが発生しますか?
ダイナミックメモリは初めてなので、これが使用可能なテストケースでない場合は、申し訳ありません。
はい。自動変数はコードブロックの終わりで破棄されます。しかし、読み続けてください。
質問のタイトルは、変数がスコープ外になったときにデストラクタが呼び出されるかどうかを尋ねます。おそらくあなたが尋ねることを意図したものは:
fooのデストラクタはmain()の最後に呼び出されますか?
あなたが提供したコードを考えると、Fooオブジェクトには動的なストレージ期間があるため、この質問に対する答えはnoです。これについては後で説明します。
ここで、自動変数とは何かに注意してください。
Foo* leedle = new Foo();
ここで、leedle
は破棄される自動変数です。 leedle
は単なるポインタです。 leedle
が指すものはnotに自動保存期間があり、破棄されません。したがって、これを行う場合:
void DoIt()
{
Foo* leedle = new leedle;
}
new leedle
によって割り当てられたメモリをリークします。
mustdelete
で割り当てられたものはすべてnew
:
void DoIt()
{
Foo* leedle = new leedle;
delete leedle;
}
これは、スマートポインターを使用することにより、はるかに単純で堅牢になります。 C++ 03の場合:
void DoIt()
{
std::auto_ptr <Foo> leedle (new Foo);
}
またはC++ 11の場合:
void DoIt()
{
std::unique_ptr <Foo> leedle = std::make_unique <Foo> ();
}
スマートポインターは上記のように自動変数として使用され、スコープから外れて破棄されると、(デストラクタ内で)delete
が指すオブジェクトを自動的に返します。したがって、上記の両方のケースで、メモリリークはありません。
ここで少し言語を整理してみましょう。 C++では、変数には保存期間があります。 C++ 03では、3つの保存期間があります。
1:自動:自動保存期間を持つ変数は、コードブロックの最後で破棄されます。
考慮してください:
void Foo()
{
bool b = true;
{
int n = 42;
} // LINE 1
double d = 3.14;
} // LINE 2
この例では、すべての変数に自動保存期間が設定されています。 b
とd
の両方がLINE 2で破棄されます。_n
はLINE 1で破棄されます。
2:static:静的ストレージ期間の変数は、プログラムが開始する前に割り当てられ、プログラムが終了すると破棄されます。
3:dynamic:動的メモリ割り当て関数(たとえば、new
)を使用して割り当てると、動的ストレージ期間の変数が割り当てられ、動的メモリを使用して破棄すると破棄されます割り当て関数(たとえば、delete
)。
上記の元の例では:
void DoIt()
{
Foo* leedle = new leedle;
}
leedle
は、自動保存期間を持つ変数であり、中括弧で破棄されます。 leedle
が指すものは動的な保存期間を持ち、上記のコードでは破棄されません。割り当てを解除するには、delete
を呼び出す必要があります。
C++ 11は、4番目のストレージ期間も追加します。
4:thread:スレッドの保存期間を持つ変数は、スレッドの開始時に割り当てられ、スレッドの終了時に割り当て解除されます。
はい。オブジェクトがスコープ外に出ると、デストラクタが呼び出されます。 [〜#〜] but [〜#〜]いいえ、この場合デストラクタは呼び出されません。なぜなら、ポインタスコープ内では、そのポインターは特定のデストラクタを持たないため、Foo
のデストラクタを間接的に呼び出すことはありません。
この例は、std::unique_ptr
やstd::shared_ptr
などのスマートポインターのアプリケーションドメインです。これらは実際のクラスであり、生のポインタとは異なり、デストラクタを持ち、(条件付きで)指示先オブジェクトでdelete
を呼び出します。
ところで、Foo
のデストラクタはbar
、bur bar
は初期化されておらず、実際のオブジェクトを指すアドレスに割り当てられていないため、削除呼び出しは未定義の動作を与えます。クラッシュ。
実際にメモリリークが発生します。スコープ外のオブジェクト(Foo *)のデストラクタが呼び出されますが、ポイント先のオブジェクト(割り当てたFoo)のデストラクタは呼び出されません。
技術的には、メインにいるので、メモリリークではありません。アプリケーションが終了しないときまで、割り当てられたすべての変数にアクセスできるためです。この点で、アレクサンドレスクを引用します(Modern C++、シングルトンに関する章から)
蓄積データを割り当て、そのデータへのすべての参照を失うと、メモリリークが発生します。ここではそうではありません。何も蓄積されていません。割り当てられたメモリに関する情報は、アプリケーションが終了するまで保持されます。さらに、すべて現代
もちろん、これはdelete
を呼び出すべきではないという意味ではありません。これは非常に悪い(そして危険な)プラクティスだからです。
最初に、コードがコンパイルされないことに注意してください。 new
は、ヒープに割り当てられたオブジェクトへのポインターを返します。必要なもの:
int main() {
Foo *leedle = new Foo();
return 0;
}
これで、new
は自動ではなく動的ストレージを使用してオブジェクトを割り当てるため、関数の最後で範囲外になりません。したがって、それも削除されず、メモリがリークしました。