web-dev-qa-db-ja.com

unique_ptrヒープとスタックの割り当て

生のポインタは、スタックまたはヒープに割り当てられたオブジェクトを指すことができます。

ヒープ割り当ての例:

// heap allocation
int* rawPtr = new int(100);
std::cout << *rawPtr << std::endl;      // 100

スタック割り当ての例:

int i = 100;
int* rawPtr = &i;
std::cout << *rawPtr << std::endl;      // 100

Auto_ptrの例を使用したヒープ割り当て:

int* rawPtr = new int(100);
std::unique_ptr<int> uPtr(rawPtr);
std::cout << *uPtr << std::endl;        // 100

Auto_ptrの例を使用したスタック割り当て:

int i = 100;
int* rawPtr = &i;
std::unique_ptr<int> uPtr(rawPtr);      // runtime error

「スマートポインタ」は、ヒープ上に動的に作成されたオブジェクトを指すために使用されることを意図していますか? C++ 11の場合、スタックに割り当てられたオブジェクトを指すために生のポインターを引き続き使用することになっていますか?ありがとうございました。

12

スマートポインタは通常、newで割り当てられ、deleteで削除されたオブジェクトを指すために使用されます。これらをこのように使用する必要はありませんが、言語構造の使用目的を推測したい場合は、それが目的のようです。

最後の例でコードがクラッシュする理由は、「deleted with delete」の部分が原因です。スコープ外になると、unique_ptrはポインタを持つオブジェクトをdeleteしようとします。スタックに割り当てられているため、これは失敗します。あなたが書いたかのように、_delete rawPtr;_

通常、ヒープオブジェクトでスマートポインタを使用するため、ヒープに割り当てて、一度にスマートポインタに変換する関数があります。 std::unique_ptr<int> uPtr = make_unique<int>(100);は、3番目の例の最初の2行のアクションを実行します。共有ポインタに一致する_make_shared_もあります。

スタックオブジェクトでスマートポインタを使用することが可能です。スマートポインターが使用する削除プログラムを指定し、deleteを呼び出さない削除プログラムを提供します。これはスタック変数であり、削除するために何もする必要がないため、削除者は何もできません。何もしない関数を呼び出すだけなら、スマートポインタのポイントは何でしょうか。これが、スタックオブジェクトで使用されるスマートポインタが一般的に見られない理由です。しかし、ここにいくつかの有用性を示す例があります。

_{
    char buf[32];
    auto erase_buf = [](char *p) { memset(p, 0, sizeof(buf)); };
    std::unique_ptr<char, decltype(erase_buf)> passwd(buf, erase_buf);

    get_password(passwd.get());
    check_password(passwd.get());
}
// The deleter will get called since passwd has gone out of scope.
// This will erase the memory in buf so that the password doesn't live
// on the stack any longer than it needs to.  This also works for
// exceptions!  Placing memset() at the end wouldn't catch that.
_
11
TrentP

ランタイムエラーは、deleteが割り当てられなかったメモリ位置でnewが呼び出されたことが原因です。

オブジェクトがすでに 動的ストレージ期間 (通常は「ヒープ」での作成として実装されている)で作成されている場合、「スマートポインタ」はランタイムエラーで示されるように正しく動作しません。

「スマートポインタ」は、ヒープ上に動的に作成されたオブジェクトを指すために使用されることを意図していますか? C++ 11の場合、スタックに割り当てられたオブジェクトを指すために生のポインターを引き続き使用することになっていますか?

何をすべきかについては、保存期間、具体的にはオブジェクトがどのように作成されたかを考えると役立ちます。

  • オブジェクトに自動保存期間(スタック)がある場合は、アドレスの取得を避け、参照を使用してください。所有権はポインタに属しておらず、参照によって所有権が明確になります。
  • オブジェクトに動的なストレージ期間(ヒープ)がある場合は、所有権を管理できるため、スマートポインターが最適です。

したがって、最後の例では、次の方が適切です(ポインターはintを所有します)。

auto uPtr = std::make_unique<int>(100);

uPtrには自動保存期間があり、スコープ外になるとデストラクタが呼び出されます。 intには動的なストレージ期間(ヒープ)があり、スマートポインターによってdeleteされます。

一般に、newdeleteの使用を避け、生のポインターの使用を避けることができます。 make_uniqueおよびmake_sharednewは必要ありません。

5
wally

「スマートポインタ」は、ヒープ上に動的に作成されたオブジェクトを指すために使用されることを意図していますか?

これらは、リークを防ぐためにヒープに割り当てられたオブジェクトを対象としています。

C++のガイドラインは、単一のオブジェクトへの参照へのプレーンポインタを使用することです(ただし、オブジェクトを所有していません)。オブジェクトのownerは、値によって、コンテナー内に、またはスマートポインターを介してオブジェクトを保持します。

3

「スマートポインタ」は、ヒープ上に動的に作成されたオブジェクトを指すために使用されることを意図していますか?

はい、しかしそれはただのデフォルトです。 std::unique_ptrには コンストラクター (そのページには(3)/(4)はありません)があり、あなたが取得したポインターを受け取ることに注意してください。どういうわけか、そしてあなたが提供する「削除者」。この場合、一意のポインタはヒープに対して何もしません(削除者がそうしない限り)。

C++ 11の場合、スタックに割り当てられたオブジェクトを指すために生のポインターを引き続き使用することになっていますか?ありがとうございました。

ポインタを「所有」しないコードでは、生のポインタを使用する必要があります。割り当てや割り当て解除に関係する必要はありません。それは、ヒープ、スタック、または他の場所を指しているかどうかに関係ありません。

これを使用するもう1つの場所は、保護された/プライベートメンバーのために、複雑な所有権パターンを持つクラスを実装する場合です。

PS:お願いします、std::auto_ptrを忘れてください...存在しなかったふりをしてください:-)

1
einpoklum