スマート ポインターとは何ですか? いつ使用すればよいですか?
ベストアンサー1
アップデート
この回答はかなり古く、当時「良かった」もの、つまりBoostライブラリが提供するスマートポインタについて説明しています。C++11以降、標準ライブラリは十分なスマートポインタ型を提供しているので、std::unique_ptr
、std::shared_ptr
そしてstd::weak_ptr
。
また、std::auto_ptr
これはスコープ付きポインターと非常によく似ていますが、コピーされるという「特別な」危険な機能も備えており、予期せず所有権が移行されます。これは
C++11 で非推奨となり、C++17 で削除されたため、使用しないでください。
std::auto_ptr<MyObject> p1 (new MyObject());
std::auto_ptr<MyObject> p2 = p1; // Copy and transfer ownership.
// p1 gets set to empty!
p2->DoSomething(); // Works.
p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.
古い答え
スマート ポインターは、指し示されているオブジェクトの有効期間を管理するために、「生の」(または「裸の」) C++ ポインターをラップするクラスです。スマート ポインターの種類は 1 つではありませんが、いずれも生のポインターを実用的な方法で抽象化しようとします。
スマート ポインターは、生のポインターよりも優先されます。ポインターを使用する必要があると思われる場合 (まず、本当に必要かどうかを考えてください)、通常はスマート ポインターを使用します。スマート ポインターを使用すると、主にオブジェクトの削除忘れやメモリ リークなど、生のポインターに伴う多くの問題を軽減できます。
生のポインターを使用する場合、プログラマーはオブジェクトが不要になったときに明示的にオブジェクトを破棄する必要があります。
// Need to create the object to achieve some goal
MyObject* ptr = new MyObject();
ptr->DoSomething(); // Use the object in some way
delete ptr; // Destroy the object. Done with it.
// Wait, what if DoSomething() raises an exception...?
それに比べて、スマート ポインターは、オブジェクトが破棄されるタイミングに関するポリシーを定義します。オブジェクトを作成する必要はありますが、破棄について心配する必要はなくなります。
SomeSmartPtr<MyObject> ptr(new MyObject());
ptr->DoSomething(); // Use the object in some way.
// Destruction of the object happens, depending
// on the policy the smart pointer class uses.
// Destruction would happen even if DoSomething()
// raises an exception
最も単純なポリシーは、スマートポインタラッパーオブジェクトのスコープに関するもので、例えば次のように実装されます。boost::scoped_ptr
またはstd::unique_ptr
。
void f()
{
{
std::unique_ptr<MyObject> ptr(new MyObject());
ptr->DoSomethingUseful();
} // ptr goes out of scope --
// the MyObject is automatically destroyed.
// ptr->Oops(); // Compile error: "ptr" not defined
// since it is no longer in scope.
}
インスタンスはコピーできないことに注意してくださいstd::unique_ptr
。これにより、ポインターが複数回 (誤って) 削除されることが防止されます。ただし、呼び出す他の関数にその参照を渡すことはできます。
std::unique_ptr
は、オブジェクトの有効期間を特定のコード ブロックに結び付けたい場合、または別のオブジェクト内にメンバー データとして埋め込んだ場合は、その他のオブジェクトの有効期間に結び付けたい場合に便利です。オブジェクトは、それを含むコード ブロックが終了するまで、またはそれを含むオブジェクト自体が破棄されるまで存在します。
より複雑なスマートポインタポリシーでは、ポインタの参照カウントを行います。これにより、ポインタのコピーが可能になります。オブジェクトへの最後の「参照」が破棄されると、オブジェクトは削除されます。このポリシーは次のように実装されます。boost::shared_ptr
そしてstd::shared_ptr
。
void f()
{
typedef std::shared_ptr<MyObject> MyObjectPtr; // nice short alias
MyObjectPtr p1; // Empty
{
MyObjectPtr p2(new MyObject());
// There is now one "reference" to the created object
p1 = p2; // Copy the pointer.
// There are now two references to the object.
} // p2 is destroyed, leaving one reference to the object.
} // p1 is destroyed, leaving a reference count of zero.
// The object is deleted.
参照カウント ポインターは、オブジェクトの有効期間が非常に複雑で、特定のコード セクションまたは別のオブジェクトに直接結び付けられていない場合に非常に便利です。
参照カウント ポインターには、ぶら下がり参照が作成される可能性があるという欠点が 1 つあります。
// Create the smart pointer on the heap
MyObjectPtr* pp = new MyObjectPtr(new MyObject())
// Hmm, we forgot to destroy the smart pointer,
// because of that, the object is never destroyed!
もう 1 つの可能性は循環参照を作成することです。
struct Owner {
std::shared_ptr<Owner> other;
};
std::shared_ptr<Owner> p1 (new Owner());
std::shared_ptr<Owner> p2 (new Owner());
p1->other = p2; // p1 references p2
p2->other = p1; // p2 references p1
// Oops, the reference count of of p1 and p2 never goes to zero!
// The objects are never destroyed!
この問題を回避するために、Boost と C++11 の両方で、weak_ptr
への弱い (カウントされない) 参照を定義する が定義されていますshared_ptr
。