私はScott MeyersのEffective Modern C++を読んでおり、彼はpimplイディオムの使用について議論しており、unique_ptr
という実装クラスを指していますが、特別なメンバー関数の問題がありますデストラクタとして)、型を完成させる必要があります。これは、delete p
が使用される前に、unique_ptr
のデフォルトのdeleterが、削除するタイプが完全かどうかを静的にアサートするためです。したがって、クラスの特別なメンバ関数は、の実装クラスの後に、の実装ファイル(コンパイラ生成ではなく)で定義する必要があります。shared_ptrを使ったPimplイディオム
最後に、使用されるスマートポインタがshared_ptr
の場合、実装ファイルに特別なメンバ関数を定義する必要はないと言います。これはカスタムのDeleterをサポートする方法に由来します。引用すると:
違いをのstd :: unique_ptrをとstd :: shared_ptrの間の行動に PIMPLポインタには、これらのスマートポインタがカスタム deletersをサポートする様々な方法に由来します。 std :: unique_ptrの場合、デリータの型はスマート ポインタの型の一部です。これによりコンパイラは小さなランタイムデータを生成することが可能になります 構造体とより速いランタイムコード。このより効率の高い結果として、 指向型は、コンパイラ生成特殊関数(たとえば、デストラクタまたは移動操作 )が使用されているときに完了する必要があります。 std :: shared_ptrの場合、 deleterのタイプはスマートポインタのタイプの一部ではありません。これにより、より大きなランタイム のデータ構造とやや遅いコードが必要になりますが、コンパイラ生成の特殊関数が使用されている場合は、ポイントタイプは完全である必要はありません。
これにもかかわらず、まだshared_ptr
がクラスが完了していないとどうしてもうまくいかないのは分かりません。 unique_ptr
が持っていたような静的アサーションが存在せず、代わりにこのアサーションがないために未定義のランタイム動作が発生する可能性があるため、shared_ptr
を使用するとコンパイラエラーが発生しないという唯一の理由のようです。
私は(C++入門を読んでから)shared_ptr
のデストラクタの実装を知らないが、私はそれが何か作品の印象集まっ:
del
はへのポインタまたは関数オブジェクトである del ? del(p) : delete p;
をカスタムディテクタ。 Cppreferenceも)それが無いカスタム削除手段とshared_ptr
デストラクタがdelete p
3を使用して明確に
T
が配列型でない場合は、削除-表現delete ptr
を使用します。 .... Yは完全なタイプでなければなりません。削除式は整形式であり、明確な振る舞いを持ち、例外を投げないようにする必要があります。
削除されたタイプが完全でなければならないという事実を強調します。PIMPLイディオムの最小例:
//widget.h
#ifndef WIDGET
#define WIDGET
#include <memory>
class Widget{
public:
Widget();
private:
struct Impl;
std::shared_ptr<Impl> pImpl;
};
#endif // WIDGET
//widget.cpp
#include <string>
#include "Widget.h"
struct Widget::Impl{
std::string name;
};
Widget::Widget(): pImpl(new Impl) {}
//main.cpp
#include <iostream>
#include "Widget.h"
int main(){
Widget a;
}
main.cpp
でWidget a
がコンパイルされると、shared_ptr
のテンプレートは、(main.cpp
内)タイプWidget
ためinstantitedおそらくshared_ptr
について得られたコンパイルデストラクタラインdelete pImpl
の実行を含んでいます私はカスタムの削除を提供していないので。しかしその時点ではまだImpl
は定義されていませんが、delete pImpl
という行が実行されます。これは、確かに、未定義の動作ですか?
shared_ptr
でpimplイディオムを使用すると、未定義の動作を避けるために、実装ファイルに特別なメンバー関数を定義する必要はありません。
私は、 'shared_ptr'デストラクタが(カスタムが与えられていなければ)直接' delete'を呼び出すと仮定していたので、それでも関数オブジェクトを介して呼び出されます。ありがとう。 – SergeantPenguin
私はこれを私の頭の中で明確にする必要がありますが、ここでDeleterが作成されていると言ったら、これは閉鎖で終わったと思いますか?私は 'shared_ptr'のための正確なデフォルトの削除の詳細を見つけることができませんでした(私は' unique_ptr'が 'std :: default_delete'を使っているのを見ましたが)。 – SergeantPenguin
@seargエフェクトは実装ではなく、指定されています。 – Yakk