8

クラスがあるとしましょう。

class BigData {...};
typedef boost::shared_ptr<BigData> BigDataPtr; 

それから私は:

BigDataPtr bigDataPtr(new BigData());

後でオブジェクトを使い終えた後、そのオブジェクトに他のユーザーがいないことを確認しました。

次のことを行っても安全ですか。

bigDataPtr->~BigDataPtr();
new (&*bigDataPtr) BigData;

これにより、追加の割り当てなしでオブジェクトをリセットできますか?

4

4 に答える 4

6

これにはいくつかの方法があります。プレースメント new を使用できます。これは、次の 2 つの理由から安全であることが保証されています。

  1. オブジェクトのメモリは既に割り当てられているため、サイズと位置が正しく調整されていることがわかります。

  2. shared_ptr非侵襲的です。その唯一の責任は、参照をカウントし、必要に応じて削除を呼び出すことです。

ただし、オブジェクトの再構築が失敗した場合 (つまり、例外がスローされた場合) に何が起こるかを考えてみてください。

bigDataPtr->~BigDataPtr();
new (bigDataPtr.get()) BigData;

次に、問題があります。デリータは、構築されていないオブジェクトで呼び出すことができ、ほぼ確実に未定義の動作につながります。私が「ほとんど」と言ったのは、deleter が何もしない可能性があるからです。その場合、すべてがうまくいきます。

新しい値を既存のオブジェクトに移動する方が安全だと思います。

*bigDataPtr = BigData(42);

reset()または、メンバー関数をBigData次のように追加します。

bigDataPtr->reset(42);

そうすれば、あなたの本当の意図が明確になり、オブジェクトの寿命についてそれほど気にする必要がなくなります。

于 2013-04-03T21:05:25.923 に答える
2

BigDataコンストラクターとデストラクターが例外をスローせずbigDataPtr、スレッド間で共有されず、動的に割り当てられたメンバー (存在する場合) へのポインターまたは参照が存在しない場合は安全ですBigData

デストラクタが例外をスローすると、部分的に破棄されたオブジェクトになる可能性があります (デストラクタのスローは一般的に推奨されず、標準のコンテナでは要素のデストラクタがスローされないようにする必要があります)。

コンストラクターがスローした場合、オブジェクトを破棄することになりますが、新しいオブジェクトを構築することはできません。

スレッド間で共有されている場合bigDataPtr、ロック規則が使用されていない限り、競合状態につながる可能性があります。

他の場所のコードが の動的に割り当てられたメンバーへの参照またはポインターを取得する場合、動的に割り当てられBigDataた新しいメンバーを作成するときに、BigDataその動的に割り当てられたメンバーが他のアドレスに割り当てられる可能性があるため、メンバーへの既存のポインターおよび参照は無効になります。

ステートメント内の疑わしい逆参照が懸念される場合はnew (&*bigDataPtr) BigData;、代わりにプレーン ポインターを使用します。

BigData* p = bigDataPtr.get();
p->~BigData();
new (p) BigData;
于 2013-04-03T20:59:19.860 に答える
2

はい、通常は安全です。 (マキシム・イェゴルシュキンの投げエッジのケースに関する観察に同意)

以下のタイプミスメッセージに注意してください

Boost は逆参照と->演算子を次のように定義します。

template<class T>
typename boost::detail::sp_dereference< T >::type boost::shared_ptr< T >::operator* () const;

template<class T>
typename boost::detail::sp_member_access< T >::type boost::shared_ptr< T >::operator-> () const;

これらのdetailビットが解決されると、これが得られます

template<class T>
T & boost::shared_ptr< T >::operator* () const

template<class T>
T * boost::shared_ptr< T >::operator-> () const 

したがって、ポイント先のオブジェクトを直接扱っています。あなたが試みていることを妨げる可能性のあるプロキシやその他の構成要素はありません。

指摘されたデータが関係しているため、コードは次のようになります。

bigDataPtr->~BigDataPtr();
new (&*bigDataPtr) BigData;

タイプミスがあるかもしれません。ただし、意図した場合:

bigDataPtr->~BigData();
new (&*bigDataPtr) BigData;

に解決されます

(BigData pointer)->~BigData();
new (&(BigData reference)) BigData;

これは合法であり、割り当てで通常発生する追加の割り当てを回避できることは正しいです。

于 2013-04-03T20:59:31.160 に答える
1

まず、コンストラクターがスローし、クラスが自明に破壊できない場合、問題が発生しますshared_ptr。これは、UB を引き起こす「削除したい」ためです。

そのため、nothrow コンストラクターを使用するか、例外をキャッチしてスマート ポインターがオブジェクトを削除しないようにすることで、これに対処する必要があります。shared_ptrには機能がないため、言うは易く行うは難しrelease()です。他のすべてが失敗した場合は呼び出すことができますterminate()が、それではユーザーに人気がありません。

オブジェクトへの参照が他にない場合は、クラスにconst非静的データ メンバー (メンバーのメンバーを含む) がないか参照されていれば機能します。理由は 3.8/7 です。

オブジェクトの有効期間が終了した後、オブジェクトが占有していたストレージが再利用または解放される前に、元のオブジェクトが占有していたストレージの場所に新しいオブジェクトが作成された場合、元のオブジェクトを指すポインター...元のオブジェクトの型が const 修飾されておらず、クラス型の場合、型が const 修飾されているか参照型である非静的データ メンバーが含まれていない場合、新しいオブジェクトの操作に使用されます。 ...

shared_ptrは、3.8/7 の条件のいずれかが壊れている場合に UB である新しいオブジェクトを操作するために使用する、まさにそのようなポインターを保持することに注意してください。壊れている可能性があるのはこれだけです。コードについて述べたことで残りをカバーしました。特に、元のオブジェクトはから派生したクラスでBigDataなく、のインスタンスとして作成する必要がありますBigData。これは、新しいオブジェクトが古いオブジェクトと同じ最も派生した型を持つ必要があるためです。

通常、オブジェクトをリセットするには、これよりも確実な方法があります。たとえば、operator=(コピーまたは移動代入演算子) を実装してから、 を記述し*bigDataPtr = BigData()ます。もちろん、それほど速くはないかもしれません。

于 2013-04-03T22:23:08.763 に答える