ここから採用。
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*}
コンストラクタは不完全で逃げることができA
、コンパイラはへの呼び出しを設定するために必要とされていない場合のみ~unique_ptr<A>()
。たとえばunique_ptr
、をヒープに置くと、不完全なを回避できますA
。この点の詳細については、ここのBarryTheHatchetの回答を参照してください。
shared_ptr
/unique_ptr
」 です。