2

スマートポインター(動的領域のリソースの所有権を取得し、使用後にリソースを解放できる)が登場する前は、リソースポインターを受け取る関数に引数として渡されたときに、動的に作成されたオブジェクトの簿記がどのように実行されたか疑問に思います。

簿記とは、「新しい」ものがある場合、その後のある時点で、それに続く「削除」があるはずであることを意味します。そうしないと、プログラムでメモリリークが発生します。

次に、Bがクラスで、void a_function(B *)がサードパーティのライブラリ関数である例を示します。

void main() {

  B* b = new B(); // line1

  a_function(b);  // line2

  ???             // line3
}

3行目で何をしますか?サードパーティの機能がメモリの割り当て解除を処理したと思いますか?そうでない場合、そして私がそうだと思うなら、私のプログラムはメモリリークに苦しんでいます。しかし、それがbによって占有されているメモリの割り当てを解除し、私もmain()でそれを行って安全を確保すると、bは実際には2回解放されてしまいます。ダブルフリーエラーが原因でプログラムがクラッシュします!

4

10 に答える 10

5

「スマートポインタ」を可能にする2つのコア言語機能、およびより一般的にはスコープにバインドされたリソース管理のイディオム(SBRM、「リソース取得は初期化」の意味でRAIIと呼ばれることもあります)は次のとおりです。

  • デストラクタ(自動gotos)

  • 制約のない変数(すべてのオブジェクトは変数として発生する可能性があります)

これらは両方ともC++の基本的なコア機能であり、常に言語の一部です。したがって、スマートポインタは常にC++で実装可能でした。

[ちなみに、これら2つの機能は、C ++では基本的に禁止されているものの、リソース割り当てと複数の出口を体系的かつ一般的な方法で処理するためにCで必要gotoであることを意味します。C++はコア言語に吸収されます。]goto

他の言語と同様に、人々が「正しい」イディオムを学び、理解し、採用するまでには長い時間がかかります。特にC++とCの歴史的なつながりを考えると、C ++プロジェクトに取り組んでいた、または取り組んでいる多くのプログラマーはCのバックグラウンドから来ており、慣れ親しんだパターンに固執する方が快適だと考えています。推奨されません(「全員と交換mallocするだけでnew、出荷の準備が整います」)。

于 2012-09-08T18:28:16.397 に答える
2

さて、これが関係しない理由についての差し迫った議論を避けて、とにかくスマートポインタを使用する必要があります...

他のすべての条件が同じである場合(カスタムアロケータやそのような派手なものはありません)、メモリを割り当てる人は誰でもメモリの割り当てを解除する必要があります。あなたの例のようなサードパーティの関数は、それが作成しなかったメモリの割り当てを解除することは絶対にしないでください。主な理由は、1)一般的に悪い習慣(ひどいコードの臭い)であり、さらに重要なのは2)方法がわからないためです。メモリは。で始まるように割り当てられました。次のことを想像してみてください。

int main()
{
    void * memory = malloc(sizeof(int));
    some_awesome_function(memory);
}

// meanwhile, in a third-party library...

void some_awesome_function(void * data)
{
    delete data;
}

異なるアロケータを使用して動作している場合はどうmalloc/freeなりますか?に使用されるアロケータは、のアロケータによって割り当てられたメモリをどう処理するかわからないnew/deleteため、ある種の潜在的なエラーを見ています。'dだった記憶はありませし、'dだった記憶もありません。これまで。deletemalloc freenewdeletemalloc

最初のポイントに関しては、サードパーティのライブラリがメモリの割り当てを解除し、手動で解放しようとした(または解放しようとしなかった)場合にどうなるかを尋ねる必要があるという事実は、まさにそのようにすべきではない理由です。 :あなたには単に知る方法がないからです。したがって、コードのどの部分も割り当ての原因であり、割り当て解除の原因でもあるという慣習が受け入れられています。誰もがこのルールに固執すれば、誰もが自分の記憶を追跡することができ、誰も推測することができなくなります。

于 2012-09-08T18:30:22.880 に答える
0

あなたはあなたが作成したものを破壊し、ライブラリはそれが作成したものを破壊します。

ライブラリとデータを共有する場合(たとえば、ファイルデータのchar *)、ライブラリのドキュメントには、データへの参照を保持するかどうかが指定されています(この場合、ライブラリでの使用が完了するまでコピーを削除しないでください)。または、データのコピーを作成します(この場合、完了時にデータを削除するのはライブラリの仕事です)。

于 2012-09-08T18:35:44.873 に答える
0

スマートポインタはC++の最初から存在していると多くの人が指摘しています。しかし、実際には、今日でもすべてのコードがそれらを使用しているわけではありません。一般的なアプローチは、参照カウントを手動で行うことです。

void main() {
  B* b = createB(); //refcount = 1 
  a_function(b);
  releaseB(b); //--refcount
}

void a_function(B* b) {
  acquireB(b); //refcount++ when we store the reference somewhere
  ...
}
于 2012-09-08T18:42:57.207 に答える
0

スマートポインタは、ポリシーの実装を容易にする方法です。同じポリシー(1人の所有者またはそれらのセットに削除する責任を帰属させる)が使用されました。ポリシーを文書化するだけで、それに応じて行動することを忘れないでください。スマートポインタは、選択したポリシーを文書化すると同時にそれを実装する方法でもあります。あなたの場合、あなたはa_functionのドキュメントを見て、それが何を要求しているかを見ました。または、文書化されていない場合は、多かれ少なかれ知識に基づいた推測を行いました。

于 2012-09-08T19:07:26.310 に答える
0

3行目で何をしますか?

のドキュメントを参照してくださいa_function。通常のルールでは、関数は、所有権や存続期間について、そう言わない限り何もしません。このようなドキュメントの必要性は、スマートポインタが利用できないCAPIを参照することでかなり明確に確立されています。

したがって、パラメータを削除するとは言わない場合は、削除しません。戻ったときを超えて、他の指定された時間まで、パラメータのコピーを保持するとは言わない場合は、そうではありません。

それがあなたがそれに応じて行動する何かを言っている場合、そしてそれが何も言っていない場合あなたdelete b(またはできれば代わりに書くB b; a_function(&b);-オブジェクトを破壊しないことによって、関数はあなたがオブジェクトを作成する方法を気にする必要がないことを観察してください、あなたは自由に決定する)。

うまくいけば、明示的に何を言っているのかがわかりますが、運が悪ければ、APIの特定の種類の関数が、パラメーターによって参照されるオブジェクトの所有権を取得するという規則があります。たとえば、それが呼び出された場合set_global_B_instance、そのポインタが保持されているのではないかという疑いがあり、設定後すぐに削除するのは賢明ではありません。

どちらの方法でも何も言わないが、コードにバグがあり、最終的にその引数がa_function呼び出されていることに気付いた場合は、ドキュメント化されたdelete人を見つけて、ドキュメントa_functionにバグを送信します。

多くの場合、その人は自分自身であることが判明します。その場合は、オブジェクトの所有権を文書化するというレッスンを学んでみてください。

スマートポインターは、コーディングエラーを回避するのに役立つだけでなく、所有権の懸念がある場合にポインターを受け入れるか返す関数に対して、ある程度の自己文書化を提供します。自己文書化がない場合は、実際の文書化があります。たとえばauto_ptr、生のポインタの代わりに関数が返される場合、ポインタdeleteで呼び出す必要があることを通知します。あなたはあなたのauto_ptrためにそれをさせることができます、あるいはあなたはそれを他のスマートポインタに割り当てることができます、あるいはあなたはそれをrelease()あなた自身でポインタと管理することができます。呼び出した関数は気にせず、何も文書化する必要はありません。関数が生のポインターを返す場合、推測する方法がないため、ポインターによって参照されるオブジェクトの存続期間について何かを通知する必要があります。

于 2012-09-08T19:12:43.593 に答える
0

答えは、サードパーティの機能のドキュメントにありますa_function()。考えられるケースは次のとおりです。

  • 関数はオブジェクト内のデータを使用するだけであり、関数呼び出しが終了した後もそのデータへの参照を保持しません(例:) printf。関数呼び出しが終了した後、オブジェクトを安全に削除できます。
  • 関数(一部の内部ライブラリオブジェクト内)は、後の呼び出し(としましょうb_function())までオブジェクトへの参照を保持します。オブジェクトを削除するのはあなたの責任ですが、呼び出すまでオブジェクトを存続させる必要がありますb_function(例:) strtok
  • 関数はオブジェクトの所有権を取得し、呼び出された後のオブジェクトの存在を保証しません(例:) free()。この場合、ドキュメントには通常、オブジェクトの作成方法が指定されています(、、malloc)。newmy_library_malloc

これらは、可能性のある多くの異なる動作のほんの一例ですが、関数が十分に文書化されている限り、正しいことを実行できるはずです。

于 2012-09-08T19:18:28.287 に答える
0

ヒントについては、CAPIを参照してください。C APIが明示的な作成および破棄関数を提供することは、かなり一般的です。これらは通常、ライブラリの正式な命名規則に従います。

あなたの例を使用すると、破棄関数として明示的にラベル付けされていない場合にパラメーターを削除/解放すると、設計a_functionが不適切になります(この場合、関数の呼び出し後にそのパラメーターを使用しないでください。ほとんどの場合、これは不適切です。所有していないオブジェクトを安全に破棄できると想定するように設計します。もちろん、スマートポインターを使用すると、所有権のメカニズム、存続期間、およびクリーンアップは、可能な場合はスマートポインターによって処理されることがよくあります。

そうです、人々はnewandを使用しdeleteました、そして私はsの前にC ++を書いていませんでしたが、明示的でプログラムでtemplate見るのがより一般的でした。スマートポインタは、オブジェクトを転送し、所有権を伝達するための非常に優れた手段ではありません。newdeletetemplate■-例外を除いて、1990年頃に導入されました(C ++が利用可能になってから7年後)。当然のことながら、コンパイラーがこれらすべての機能をサポートし、人々がコンテナーを実装してそれらの実装を改善するには、ある程度の時間がかかりました。テンプレートの前にそれは可能でしたが、言語がテンプレートの前にジェネリックスをサポートしていなかったため、任意のタイプのコンテナーを実装/クローンすることが常に実用的であるとは限らなかったことに注意してください。もちろん、具象型を持つ具象クラスは、当時は型が不変だったスマートポインターの仕組みを簡単に実現できますが、ジェネリックスが利用できない場合は、コードの重複が発生します。

しかし、今日でも、明確にラベル付けされていない限り、スマートポインタパラメータのコンテンツオブジェクトが置き換えられたり破棄されたりするのは珍しい設計です。スマートポインタが保持しているオブジェクトではなく、パラメータとしてスマートポインタを渡すことも珍しいため、この可能性も低くなります。そのため、メモリ関連のバグの数はそれ以降減少していますが、注意と適切な所有権の慣習を遵守する必要があります。

于 2012-09-08T19:28:24.830 に答える
0

簡単な答え:ドキュメントを読んでください。これはCインターフェースでは一般的なことであり、関数がオブジェクトの所有権を主張する場合、リソース管理はインターフェースの重要な部分であるため、文書化されます。

于 2012-09-08T19:30:43.440 に答える
0

スマートポインタがない場合、プログラマーは通常、割り当てたエンティティが割り当て解除を担当するというルールを採用します。

あなたの例では、サードパーティの関数が渡されたポインタを削除することは(有効なコードであるにもかかわらず)通常は悪い動作と見なされ、3行目で削除することが期待されます。

これはプログラマー間の社会契約であり、コンパイラーは通常これを強制しません。

于 2012-09-08T21:13:30.430 に答える