以下の私のコードを考えてみましょう。一意のポインターについての私の理解は、1つの変数またはオブジェクトを参照するために使用できる一意のポインターは1つだけであるということでした。私のコードでは、同じ変数にアクセスするunique_ptrが複数あります。
ポインターは作成から完全な所有権を持つ必要があるという点で、私が知っているスマートポインターを使用する正しい方法ではないことは明らかです。しかし、それでも、なぜこれが有効でコンパイルエラーがないのですか?ありがとう。
#include <iostream>
#include <memory>
using namespace std;
int main()
{
int val = 0;
int* valPtr = &val;
unique_ptr <int> uniquePtr1(valPtr);
unique_ptr <int> uniquePtr2(valPtr);
*uniquePtr1 = 10;
*uniquePtr2 = 20;
return 0;
}
しかし、それでも、なぜこれが有効なのですか
not有効です! _std::unique_ptr
_のデストラクタは自動保存期間でオブジェクトを解放するため、これは未定義の動作です。
実際には、プログラムはint
オブジェクトを3回破壊しようとします。最初に_uniquePtr2
_を介して、次に_uniquePtr1
_を介して、次にval
自体を介して。
コンパイルエラーがありませんか?
このようなエラーは通常、コンパイル時に検出できないため、次のようになります。
_unique_ptr <int> uniquePtr1(valPtr);
unique_ptr <int> uniquePtr2(function_with_runtime_input());
_
この例では、function_with_runtime_input()
は多くの複雑なランタイム操作を実行し、最終的にはvalPtr
が指す同じオブジェクトへのポインターを返します。
_std::unique_ptr
_を正しく使用すると、ほとんどの場合_std::make_unique
_を使用して、このようなエラーを防ぎます。
クリスチャンハックルの優れた答えへの単なる追加:
_std::unique_ptr
_は、ポインターの [〜#〜] raii [〜#〜] を保証するために導入されました。つまり、生のポインタとは逆に、自分で破壊する必要はもうありません。生のポインターの全体の管理は、スマートポインターによって行われます。 delete
を忘れたために発生したリークはもう発生しません。
_std::unique_ptr
_が_std::make_unique
_によってのみ作成されることを許可する場合、割り当てと割り当て解除に関しては絶対に安全であり、もちろんコンパイル時にも検出できます。
しかし、そうではありません。_std::unique_ptr
_は、生のポインターを使用して構築することもできます。その理由は、ハードポインターを使用して構築できると、_std::unique_ptr
_がはるかに便利になるためです。これが不可能な場合、例えばChristianHacklのfunction_with_runtime_input()
によって返されるポインターは、最新のRAII環境に統合することはできません。自分で、破壊の世話をする必要があります。
もちろん、これの欠点は、あなたのようなエラーが発生する可能性があることです。_std::unique_ptr
_では破壊を忘れることはできませんが、誤った複数の破壊は常に可能です(CHがすでに述べたように、コンパイラで追跡することは不可能です)。生のポインターコンストラクター引数を使用して作成しました。 _std::unique_ptr
_は論理的に生のポインタの「所有権」を取得することに常に注意してください。つまり、_std::unique_ptr
_自体を除いて他の誰もポインタを削除できないということです。
経験則として、それは言うことができます:
std::unique_ptr
_を使用して_std::make_unique
_を作成してください。std::unique_ptr
_を作成した後、生のポインターに触れないでください。std::unique_ptr
_が提供された生のポインタの所有権を取得することに常に注意してくださいval
のように、それらは不可避的に自動的に破棄されるため)。new
によって作成された生のポインターのみを使用して_std::unique_ptr
_を作成します。new
以外で作成された生のポインターを使用して_std::unique_ptr
_を作成する必要がある場合は、ハードポインターの作成者と一致するカスタム削除ツールを_std::unique_ptr
_に追加します。例としては、(Cベースの) FreeImage ライブラリの画像ポインタがあります。これは常にFreeImage_Unload()
によって破棄する必要があります。これらのルールのいくつかの例:
_// Safe
std::unique_ptr<int> p = std::make_unique<int>();
// Safe, but not advisable. No accessible raw pointer exists, but should use make_unique.
std::unique_ptr<int> p(new int());
// Handle with care. No accessible raw pointer exists, but it has to be sure
// that function_with_runtime_input() allocates the raw pointer with 'new'
std::unique_ptr<int> p( function_with_runtime_input() );
// Safe. No accessible raw pointer exists,
// the raw pointer is created by a library, and has a custom
// deleter to match the library's requirements
struct FreeImageDeleter {
void operator() (FIBITMAP* _moribund) { FreeImage_Unload(_moribund); }
};
std::unique_ptr<FIBITMAP,FreeImageDeleter> p( FreeImage_Load(...) );
// Dangerous. Your class method gets a raw pointer
// as a parameter. It can not control what happens
// with this raw pointer after the call to MyClass::setMySomething()
// - if the caller deletes it, your'e lost.
void MyClass::setMySomething( MySomething* something ) {
// m_mySomethingP is a member std::unique_ptr<Something>
m_mySomethingP = std::move( std::unique_ptr<Something>( something ));
}
// Dangerous. A raw pointer variable exists, which might be erroneously
// deleted multiple times or assigned to a std::unique_ptr multiple times.
// Don't touch iPtr after these lines!
int* iPtr = new int();
std::unique_ptr<int> p(iPtr);
// Wrong (Undefined behaviour) and a direct consequence of the dangerous declaration above.
// A raw pointer is assigned to a std::unique_ptr<int> twice, which means
// that it will be attempted to delete it twice.
// This couldn't have happened if iPtr wouldn't have existed in the first
// place, like shown in the 'safe' examples.
int* iPtr = new int();
std::unique_ptr<int> p(iPtr);
std::unique_ptr<int> p2(iPtr);
// Wrong. (Undefined behaviour)
// An unique pointer gets assigned a raw pointer to a stack variable.
// Erroneous double destruction is the consequence
int val;
int* valPtr = &val;
std::unique_ptr<int> p(valPtr);
_
このコード例は少し人工的なものです。 unique_ptr
は通常、実際のコードではこのように初期化されません。使用する - std::make_unique
または初期化unique_ptr
生のポインタを変数に格納せずに:
unique_ptr <int> uniquePtr2(new int);