2016-11-15 16 views
4

私は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.cppWidget aがコンパイルされると、shared_ptrのテンプレートは、(main.cpp内)タイプWidgetためinstantitedおそらくshared_ptrについて得られたコンパイルデストラクタラインdelete pImplの実行を含んでいます私はカスタムの削除を提供していないので。しかしその時点ではまだImplは定義されていませんが、delete pImplという行が実行されます。これは、確かに、未定義の動作ですか?

shared_ptrでpimplイディオムを使用すると、未定義の動作を避けるために、実装ファイルに特別なメンバー関数を定義する必要はありません。

答えて

6

共有ポインタのためデリータが、ここで作成されます。

Widget::Widget(): pImpl(new Impl) {} 

その時点まで、すべての共有ポインタがstd::funciton<void(Impl*)>のと同じですしています。

T*shared_ptrを作成すると、それはデリータを書き込んで、std::functionに相当するものに保存します。その時点でタイプは完全でなければなりません。

したがって、Implの後に定義する必要がある機能は、T*から何らかの種類のpImplを作成するものだけです。

+0

私は、 'shared_ptr'デストラクタが(カスタムが与えられていなければ)直接' delete'を呼び出すと仮定していたので、それでも関数オブジェクトを介して呼び出されます。ありがとう。 – SergeantPenguin

+0

私はこれを私の頭の中で明確にする必要がありますが、ここでDeleterが作成されていると言ったら、これは閉鎖で終わったと思いますか?私は 'shared_ptr'のための正確なデフォルトの削除の詳細を見つけることができませんでした(私は' unique_ptr'が 'std :: default_delete'を使っているのを見ましたが)。 – SergeantPenguin

+0

@seargエフェクトは実装ではなく、指定されています。 – Yakk

関連する問題