16

pImpl イディオムを使用する場合、a のboost:shared_ptr代わりに aを使用することをお勧めしstd::auto_ptrます。ブーストバージョンはより例外に優しいと読んだことがありますか?

class Foo
{
public:
    Foo();
private:
    struct impl;
    std::auto_ptr<impl> impl_;
};

class Foo
{
public:
    Foo();
private:
    struct impl;
    boost::shared_ptr<impl> impl_;
};

[編集] std::auto_ptr<> を使用することは常に安全ですか、それとも別のブースト スマート ポインターが必要な状況がありますか?

4

9 に答える 9

38

これには std::auto_ptr を実際に使用しないでください。std::auto_ptr を宣言した時点ではデストラクタが表示されないため、適切に呼び出されない可能性があります。これは、pImpl クラスを前方宣言し、別のファイルのコンストラクター内にインスタンスを作成していることを前提としています。

boost::scoped_ptrを使用する場合(ここでは shared_ptr は必要ありません。pimpl を他のオブジェクトと共有することはありません。これはscoped_ptrが noncopyable であることによって強制されます)、呼び出した時点で表示される pimpl デストラクタのみが必要です。 scoped_ptr コンストラクター。

例えば

// in MyClass.h

class Pimpl;

class MyClass 
{ 
private:
    std::auto_ptr<Pimpl> pimpl;

public: 
    MyClass();
};

// Body of these functions in MyClass.cpp

ここで、コンパイラは MyClass のデストラクタを生成します。auto_ptr のデストラクタを呼び出す必要があります。auto_ptr デストラクタがインスタンス化される時点で、Pimpl は不完全な型です。そのため、Pimpl オブジェクトを削除するときに auto_ptr デストラクタに入ると、Pimpl デストラクタを呼び出す方法がわかりません。

boost::scoped_ptr (および shared_ptr) にはこの問題はありません。これは、scoped_ptr (または reset メソッド) のコンストラクターを呼び出すと、delete を呼び出す代わりに使用される関数ポインターと同等のものも作成されるためです。ここで重要な点は、Pimpl が不完全型でない場合に解放関数をインスタンス化することです。補足として、shared_ptr を使用すると、カスタムの割り当て解除関数を指定できるため、GDI ハンドルなど、必要なものに使用できますが、ここでのニーズには過剰です。

本当に std::auto_ptr を使用したい場合は、Pimpl が完全に定義されているときに MyClass.cpp で MyClass デストラクタを定義していることを確認して、特別な注意を払う必要があります。

// in MyClass.h

class Pimpl;

class MyClass 
{ 
private:
    std::auto_ptr<Pimpl> pimpl;

public: 
    MyClass();
    ~MyClass();
};

// in MyClass.cpp

#include "Pimpl.h"

MyClass::MyClass() : pimpl(new Pimpl(blah))
{
}

MyClass::~MyClass() 
{
    // this needs to be here, even when empty
}

コンパイラは、すべての MyClass メンバーを事実上空のデストラクタ内で破壊するコードを生成します。そのため、auto_ptr デストラクタがインスタンス化された時点で、Pimpl はもはや不完全ではなく、コンパイラはデストラクタを呼び出す方法を認識しています。

個人的には、すべてが正しいことを確認する手間をかける価値はないと思います。また、後で誰かがやってきて、冗長に見えるデストラクタを削除してコードを整理するリスクもあります。したがって、この種のことについては、boost::scoped_ptr を使用する方が全体的に安全です。

于 2008-11-22T11:57:06.457 に答える
12

を使用する傾向がありますauto_ptr。必ずクラスをコピー不可にします (プライベート コピーの ctor と operator= を宣言するか、 inherit を宣言しますboost::noncopyable)。を使用するauto_ptr場合、body が空であっても、非インライン デストラクタを定義する必要があることが 1 つの問題です。(これは、コンパイラにデフォルトのデストラクタを生成させると、implへの呼び出しが生成されたときに不完全な型になり、delete impl_未定義の動作が呼び出されるためです)。

auto_ptrブーストポインターとの間で選択することはほとんどありません。標準ライブラリの代替手段で十分な場合、スタイル上の理由からブーストを使用しない傾向があります。

于 2008-11-22T10:24:05.177 に答える
4

ブーストの代替std::auto_ptrは ですboost::scoped_ptr。との主な違いauto_ptrは、コピーできないことboost::scoped_ptrです。

詳しくはこちらのページをご覧ください。

于 2008-11-22T10:25:36.797 に答える
4

boost::shared_ptr は、pimpl イディオムで動作するように特別に調整されています。主な利点の 1 つは、pimpl を保持するクラスのデストラクタを定義できないことです。共有所有権ポリシーは、おそらく長所と短所の両方です。ただし、後でコピーコンストラクターを適切に定義できます。

于 2008-12-26T19:14:06.977 に答える
1

コピー可能なクラスが必要な場合は、 を使用しますscoped_ptr。これにより、コピーが禁止されるため、クラスがデフォルトで間違って使用されにくくなります ( を使用する場合と比較してshared_ptr、コンパイラは独自にコピー機能を発行shared_ptrしません。あなたが何をしているのかを知っていれば[ウィザードにとっても十分なケースです]、突然何かのコピーがその何かを変更すると、奇妙な動作が発生します)。

class CopyableFoo {
public:
    ...
    CopyableFoo (const CopyableFoo&);
    CopyableFoo& operator= (const CopyableFoo&);
private:
    scoped_ptr<Impl> impl_;
};

...
CopyableFoo (const CopyableFoo& rhs)
    : impl_(new Impl (*rhs.impl_))
{}
于 2011-08-10T15:02:33.153 に答える
1

あなたが本当に衒学的であるなら、メンバーを使用するために、それが使用される時点でのテンプレートパラメーターのauto_ptr完全な定義を必要としないという絶対的な保証はありません。auto_ptrそうは言っても、これが機能しないのを見たことはありません。

1 つのバリエーションは、を使用することconst auto_ptrです。これは、初期化子リスト内の新しい式で「pimpl」を構築でき、コンパイラがデフォルトのコピー コンストラクタと代入メソッドを生成できないことを保証する限り機能します。外側のクラスの非インライン デストラクタを提供する必要があります。

他の条件が同じであれば、移植性を高めるため、標準ライブラリのみを使用する実装をお勧めします。

于 2008-11-22T11:12:26.220 に答える
0

外部クラスをコピーすると、そのポインタが突然失われる可能性があるため、shared_ptr は pImpl の auto_ptr よりもはるかに適しています。

shared_ptr を使用すると、前方宣言型を使用できるため、機能します。auto_ptr は、前方宣言型を許可しません。scoped_ptr もそうではありません。外部クラスがとにかくコピー不可になり、ポインターが 1 つしかない場合は、通常のクラスである可能性があります。

pImpl で侵入参照カウントを使用し、外部クラスにそのコピーを呼び出してその実装でセマンティクスを割り当てさせることについては、多くのことが言えます。これが真のベンダー (クラスを提供する) モデルであると仮定すると、ベンダーがユーザーに shared_ptr を使用すること、または shared_ptr の同じバージョン (boost または std) を使用することを強制しない方がよいでしょう。

于 2011-03-09T17:07:04.903 に答える
-8

C++ にはたくさんのチャンスがあります :) どちらの自動ポインタも実際に使用する必要はありません。コンストラクタとデストラクタ)。

複雑にしないでおく。

于 2008-11-22T10:20:04.220 に答える