ここから採用。
C++ 標準ライブラリのほとんどのテンプレートでは、完全な型でインスタンス化する必要があります。ただしshared_ptr
、 とunique_ptr
は部分的な例外です。すべてではありませんが、一部のメンバーは不完全な型でインスタンス化できます。これの動機は、未定義の動作を危険にさらすことなく、スマート ポインターを使用してpimplなどのイディオムをサポートすることです。
不完全な型があり、それを呼び出すと、未定義の動作が発生する可能性がありますdelete
。
class A;
A* a = ...;
delete a;
上記は法定コードです。コンパイルされます。コンパイラは、上記のような上記のコードに対して警告を発する場合としない場合があります。実行すると、おそらく悪いことが起こります。運が良ければ、プログラムがクラッシュします。~A()
ただし、より可能性の高い結果は、プログラムが呼び出されないため、黙ってメモリをリークすることです。
上記の例で使用auto_ptr<A>
しても役に立ちません。生のポインターを使用した場合と同じ未定義の動作が得られます。
それにもかかわらず、特定の場所で不完全なクラスを使用することは非常に便利です! これはどこにshared_ptr
あり、unique_ptr
助けます。これらのスマート ポインターのいずれかを使用すると、完全な型が必要な場合を除き、不完全な型を回避できます。そして最も重要なことは、完全な型が必要な場合に、その時点で不完全な型でスマート ポインターを使用しようとすると、コンパイル時にエラーが発生することです。
未定義の動作はもうありません:
コードがコンパイルされる場合は、必要なすべての場所で完全な型を使用しています。
class A
{
class impl;
std::unique_ptr<impl> ptr_; // ok!
public:
A();
~A();
// ...
};
shared_ptr
さまざまな場所でunique_ptr
完全なタイプが必要です。理由は不明で、動的デリータと静的デリータに関係しています。正確な理由は重要ではありません。実際、ほとんどのコードでは、完全な型が必要な場所を正確に知ることはあまり重要ではありません。コーディングするだけで、間違っている場合はコンパイラーが教えてくれます。
ただし、参考になる場合は、完全性要件のいくつかのメンバーshared_ptr
を文書化した表を次に示します。unique_ptr
メンバーが完全なタイプを必要とする場合、エントリには「C」があり、それ以外の場合、テーブル エントリは「I」で埋められます。
Complete type requirements for unique_ptr and shared_ptr
unique_ptr shared_ptr
+------------------------+---------------+---------------+
| P() | I | I |
| default constructor | | |
+------------------------+---------------+---------------+
| P(const P&) | N/A | I |
| copy constructor | | |
+------------------------+---------------+---------------+
| P(P&&) | I | I |
| move constructor | | |
+------------------------+---------------+---------------+
| ~P() | C | I |
| destructor | | |
+------------------------+---------------+---------------+
| P(A*) | I | C |
+------------------------+---------------+---------------+
| operator=(const P&) | N/A | I |
| copy assignment | | |
+------------------------+---------------+---------------+
| operator=(P&&) | C | I |
| move assignment | | |
+------------------------+---------------+---------------+
| reset() | C | I |
+------------------------+---------------+---------------+
| reset(A*) | C | C |
+------------------------+---------------+---------------+
ポインター変換を必要とする操作には、unique_ptr
との両方の完全な型が必要ですshared_ptr
。
コンストラクターは、コンパイラーが への呼び出しをセットアップする必要がない場合にのみunique_ptr<A>{A*}
、不完全なものを回避できます。たとえば、 をヒープに置くと、不完全な を回避できます。この点の詳細については、BarryTheHatchet の回答(こちら) を参照してください。A
~unique_ptr<A>()
unique_ptr
A