3

質問のタイトルが少し頭を悩ませているように見えることは知っていますが、これを一言で言う方法が本当にわかりません. 私が何を意味するかをお見せします:

void f(T *obj)
{
    // bla bla
}
void main()
{
    f(new T());
}

私の知る限り、(ほぼ) すべての new には削除が必要であり、これにはポインタ (new によって返される) が必要です。この場合、new によって返されるポインターはどこにも格納されません。それで、これはメモリリークでしょうか?

C++ は、関数の終了後にオブジェクトを削除する何らかの魔法 (プログラマーには見えない) を機能させますか?それとも、この方法は常に悪い考えですか?

4

7 に答える 7

7

There is no particular magic and delete will not be called automatically.

It is definitely not "always a bad idea" - if function takes ownership of an object in some form it is perfectly valid way for calling such function:

container.AddAndTakeOwnership(new MyItem(42));
于 2012-10-25T21:21:24.347 に答える
5

この場合、new によって返されるポインターはどこにも格納されません。それで、これはメモリリークでしょうか?

いいえ、必ずしもメモリ リークではありません。ポインタは、への引数として格納されますf

void f(T *obj)
//        ^^^  here pointer is "stored"
{
    // bla bla
    delete obj; // no memory leak if delete will be called on obj
}
void main()
{
    f(new T());
  //  ^^^^^^^  this "value" will be stored as an argument to f
}

C++ は、関数の終了後にオブジェクトを削除するある種の魔法 (プログラマーには見えない) を機能させますか?それとも、この慣行は単に常に悪い考えですか?

あなたの例には魔法はありません。私が示したように、削除は明示的に呼び出す必要があります。

スマート ポインターを使用することをお勧めします。そうすれば、C++ の「魔法」が機能し、削除は必要ありません。

void f(std::unique_ptr<T> obj)
{
    // bla bla
}
void main()
{
    f(new T());
}
于 2012-10-25T21:33:15.597 に答える
5

はい、それは常に悪い考えです。関数とコンストラクターは、所有権のセマンティクスについて明示する必要があり、渡されたポインターの所有権を取得または共有することを期待する場合は、それぞれ受け取るstd::unique_ptr必要std::shared_ptrがあります。

標準ライブラリであっても、所有権のセマンティクスを持つ生のポインターを取るレガシー API はたくさんありますが (たとえば、所有権localeを持つコンストラクターFacet *など)、新しいコードではこれを避ける必要があります。

unique_ptrorを構築する場合でも、キーワードのshared_ptr使用を避けることができます。後者の場合は使用し、前者の場合は関数テンプレートを作成します。これはすぐに言語に追加されるはずです。newmake_sharedmake_unique

于 2012-10-25T21:27:06.580 に答える
4

示されているコードでは、メモリ リークが発生します。C++ には、ガベージ コレクションを提供する特殊なフレームワークを明示的に使用しない限り、ガベージ コレクションはありません。

この理由は、C/C++ でのメモリの管理方法に関係しています。あなたの例のようなローカル変数の場合、オブジェクトのメモリはオペレーティング システム (malloc) から直接要求され、オブジェクトへのポインターがスタックに存在します。C/C++ は任意の複雑なポインター演算を実行できるため、コンパイラーは、オブジェクトへの他のポインターが存在するかどうかを知る方法がないため、関数 f() が終了したときにメモリーを再利用できません。

リークを自動的に防止するには、マネージ ヒープからメモリを割り当てる必要があり、このヒープへのすべての参照を注意深く追跡して、特定のオブジェクトがいつ使用されなくなったかを判断する必要があります。この機能を得るには、ポインター演算を行う C の機能を放棄する必要があります。

たとえば、コンパイラが obj へのすべての通常の参照が機能していないことを魔法のように判断し、オブジェクトを削除した (メモリを解放した) とします。void* ptr = (&& &&(&&& *obj)/2++ - currenttime() - 567 + 3^2 % 52) など; コンパイラは、この ptr が obj を指しているかどうかをどのように知るのでしょうか? 知る方法はありません。これが、ガベージ コレクションがない理由です。両方ではなく、ガベージ コレクションまたは複雑なランタイム ポインター演算のいずれかを使用できます。

于 2012-10-25T21:29:50.760 に答える
2

This is often used when your function f() is storing the object somewhere, like an array (or any other data container) or a simple class member; in which case, deletion will (and must) take place somewhere else.

Otherwise is not a good idea cause you will have to delete it manually at the end of the function anyway. In that case, you can just declare an automatic (on the stack) object and pass it by pointer.

于 2012-10-25T21:20:53.207 に答える
1

No magic. In your case after f is called main returns back to CRT's main and eventually the OS will clean up the "leak". Its not necessarily a bad idea, it could be giving f the ownership and it is up to f to do stuff and eventually delete. Some make call it bad practice but its probably out there in the wild.

Edit: Although I see that code no more dangerous than:

void f(T *obj)
{
    // bla bla
}
void main()
{
    T* test = new T ();
    f(test);
}

Principally it is the same. Its saying to f, here is a pointer to some memory, its yours you look after it now.

于 2012-10-25T21:21:09.903 に答える
1

C++ では、関連付けられた所有者のセマンティクスがないため、ポインターの受け渡しはお勧めできません (つまり、ポインターの所有者や、ポインターの削除の責任者がわからない)。したがって、(ポインターを受け取る) 関数がある場合、関数がポインターのクリーンアップを担当するかどうかを明確に文書化する必要があります。もちろん、このようなドキュメントは、ユーザーが読む必要があるため、非常にエラーが発生しやすいものです。

C++ プログラムでは、モノの所有権セマンティクスを記述するオブジェクトを渡すのがより一般的です。

  1. 参照渡し
    関数はオブジェクトの所有権を取得していません。オブジェクトを使用するだけです。
  2. std::auto_ptr (または std::unique_ptr)
    を渡す 関数には、ポインターとポインターの所有権が渡されます。
  3. std::shared_ptr
    を渡します 関数には、ポインターの共有所有権が渡されます。

これらの手法を使用することで、所有権のセマンティクスを文書化するだけでなく、使用されるオブジェクトによってオブジェクトの寿命も自動的に制御されます (したがって、関数が delete を呼び出す必要がなくなります)。

deleteその結果、最新の C++ コードでを手動で呼び出すことは実際には非常にまれです。

だから私は次のように書いたでしょう:

void f(std::unique_ptr<T> obj)
{
    // bla bla
}
int main()
{
    f(std::unique_ptr<T>(new T()));
}
于 2012-10-25T21:49:47.120 に答える