あなたが質問に追加したコードに気づきました。SharedPointers はそれを行いません。
SharedPointers は、「データ共有」に関して「共有」されるのではなく、「参照カウント」に関して「共有」されます。あなたの用語での「データ共有」は..ポインターによって行われます。「共有」したいものは何でも、ポインタで参照してください。その後、変更すると、全員が更新を確認できます。しかし、それへのポインターではなく、それを変更する必要があります。
つまり、ここではポインターを更新し、ポインターが更新されるのを全員に見てもらいたいということです。したがって、ポインターをポインターごとに保持する必要があります。
つまり、 を保持する代わりにvector<shared_ptr<Base>>
、 を保持しvector<shared_pointer<Base>*>
ます。
ここで、オブジェクトを新しいインスタンスに「グローバルに置き換える」場合は、shptr<Base>
保持されているポインターを別の新しい shptr に置き換えます。
生のポインターが気に入らない場合はvector<shared_pointer<sahred_pointer<Base>>
、外側をそのままにして、内側を使用して .reset することもできます。アウターのコピーを入手した人は誰でも、インナーのアップデートを見ることができます。
// obj_array and container are a vector<shared_ptr<Base>*>
// or a vector<shared_ptr<shared_ptr<Base>>>
obj_array.push_back(new std::shared_ptr<Base>(new Base()); // note the 'new'
container.push_back(obj_array[0]]);
(*obj_array[0]) .reset(Child_A()); // note the '*'
obj_array[0] -> reset(Child_A()); // or, just in short
編集#2:
あなたのコメントの後に「もう1点、obj_arrayはポインターからポインターへのポインターのベクトルである必要はありませんvector<shared_ptr<Base>>
。単に a である可能性があります。間違っている場合は修正してください」:
それはあなたが何を達成したいかによります。好きなように物事をベクトルに保つことができます-それはそれらの使用の特定のシナリオにのみ影響します. ここで、非常に抽象的な設定について説明します。どういうわけかいくつかのものを保持するメイン コンテナーがあります。A、B、C と呼ばれるアプリケーションの部分は、定期的にこれらのものを受け取り、それらに対して何かを実行します。パーツBとCは、何らかの目的で物事を内部的に記憶する傾向がある場合があります. ここで、次のように仮定します。
- ケース 1: ベクトルは
Base*
オブジェクトを保持します。
- ケース 2: ベクトルは
shared_ptr<Base>
オブジェクトを保持します。
- ケース 3: ベクトルは Base** を保持します
- ケース 4: ベクトル保持
shptr<shptr<Base>>
もちろん、もっと多くのケースが考えられますが、それをトリミングしましょう。これで、アプリが実行され、メイン コンテナーに既にいくつかのオブジェクトが含まれています。モジュール A、B、C はすでに何かを処理しており、おそらくモジュール B と C はすでにいくつかのオブジェクトを記憶しています。そして今、アプリケーションは、メイン コンテナの 5 番目のアイテムを に置き換える必要があるところまで来ましたnew Bar()
。
ケース 1:
ベクトルは ですBase*
。Bar はもちろん Base を実装しているため、 new Bar() は に直接代入できvector[4]
ます。もちろん、古い要素をどうするかはあなたが決めています。削除するか忘れますか?次に、vector[4]=new Bar()
が実行されます。今後、このメイン ベクトルを読み取るすべてのユーザーは、5 番目の位置に新しいオブジェクトが表示されます。
しかし、これで問題は終わりではありません。モジュール B と C はまだ古いオブジェクトを認識している可能性があります。ベクトルの要素が Base* (生のポインター) だったので、これらの B/C は古いオブジェクトへのポインターの生の値をコピーしたため、現在の唯一の解決策は、B と C にも置換を実行するよう明示的に指示することです。
したがって、ケース 1 は次のコードのようなものとして終了します。初期セットアップの例、ランタイム操作の例、最終的なクリーンアップの例の 3 つの段階に分かれています。
vector<Base*> vector;
vector.resize( 10 );
///// .... later ....
Base* olditem = vector[ 4 ];
Base* newitem = new Bar();
bool iWillDeleteTheOld = well_somehow_decide();
vector[4] = newitem;
moduleB->updateAfterReplace(olditem, newitem, iWillDeleteTheOld);
modulec->updateAfterReplace(olditem, newitem, iWillDeleteTheOld);
if(iWillDeleteTheOld)
delete olditem;
///// .... later ....
for( ... idx ...)
delete vector[idx];
vector.resize(0);
モジュール B/C はベクター項目を として読み取りBase*
、それをキャッシュする場合は としてキャッシュしBase*
ます。
このコードによりupdateAfterReplace
、古いオブジェクトをまだ記憶している可能性のあるすべての「モジュール」に追加の関数を記述することに注意してください。そして、ここで古いオブジェクトを削除しようとする場合に備えて、古いオブジェクトを削除しようとしないように内部を調整する必要があります。かどうかを伝えることで、ここで解決しました。私がやるとわかっていれば、彼らは古いオブジェクトの削除フェーズをスキップします。ただし、削除しないことにした場合 (iwilldelete=false)、彼らは自分で削除することを決定する可能性があります..updateAfterReplace
iWillDeleteTheOld
しかし、それは適切な所有権管理の問題であり、ここでは触れません。
ケース 2:
ベクトルは ですshared_ptr<Base>
。Bar はもちろん Base を実装しているため、 new Bar() は直接代入可能ですvector[4]
(変更なし)。もちろん、古い要素をどうするかはあなたが決めています (変更なし)。
(変更) しかし、ポインターを として保持しているのでshared_ptr
、所有権に問題はありません。ポインターを上書き/解放するだけで、あとは shptr が処理します。誰かがオブジェクトを使用すると、オブジェクトは削除されます。まだ使用されている場合は、それを維持します。
次に、vector[4]=new Bar()
が実行されます。今後、このメイン ベクトルを読み取るすべてのユーザーは、5 番目の位置に新しいオブジェクトが表示されます。(変化なし)
しかし、これで問題は終わりではありません。モジュール B と C はまだ古いオブジェクトを認識している可能性があります。ベクトルの要素が だったsharedptr<Base>
ので、これらの B/C は shared_ptr-to-old-object をコピーしたため、現在の唯一の解決策は、B と C にも置換を実行するよう明示的に指示することです。(変更なし)
したがって、ケース 2 は次のように終了します。
vector<shared_ptr<Base>> vector;
vector.resize( 10 );
///// .... later ....
sharedptr<Base> olditem = vector[4];
sharedptr<Base> newitem = new Bar();
vector[4].reset( newitem ); // <- THE LINE
moduleB->updateAfterReplace(olditem, newitem);
modulec->updateAfterReplace(olditem, newitem);
///// .... later ....
vector.resize(0);
モジュール B/C はベクター項目を として読み取りsharedptr<Base>
、それをキャッシュする場合は としてキャッシュしsharedptr<Base>
ます。を減らすとBase*
、ケースは Case1 に変換されます。
「削除について決定する」と「オブジェクトの削除」と「i-tell-you-that-i-delete-it is gone. This is the benefit of
sharedptr」の方法に注意してください。ただし、まだ古いオブジェクトを保持している可能性のある他のすべてのキャッシュを手動で更新する必要があります。
これは、THE LINE が shared_ptr を上書きするだけでなく、「おそらく削除」フェーズも実行するためです。内部参照カウント メカニズムから shared_ptr を切り離し、カウントがゼロになった場合は、オブジェクトを削除します。問題はここにあります。デタッチします。vector[4] の shptrからコピーされた他のすべてのsharedptr
ものは、独自の refcounting グループを形成しており、古いオブジェクトをまだ覚えています。彼らは内容を更新しませんでした。それらはまとめて refcount=3 から refcount=2 に減少しました。
ケース 3:
ベクトルは ですBase**
。Bar はもちろん Base を実装しているため、 new Bar() は に直接代入できませんvector[4]
: vector はポインターツーポインターを保持するようになったため、追加の逆参照も必要になります ( change )。もちろん、古い要素をどうするかはあなたが決めています (変更なし)。削除するか忘れますか?(変化なし)
次に、*vector[4]=new Bar()
が実行されます。今後、このメイン ベクトルを読み取るすべてのユーザーは、5 番目の位置に新しいオブジェクトが表示されます。(変化なし)
そして、これで問題は終わりです。(変更)
したがって、ケース 3 は次のように終了します。
vector<Base**> vector;
for(int i = 0; i<10; ++i)
vector.push( new Base* );
///// .... later ....
Base* olditem = * vector[4]; // note the dereference
Base* newitem = new Bar();
bool iWillDeleteTheOld = well_somehow_decide();
* vector[4] = newitem; // note the dereference
if(iWillDeleteTheOld)
delete olditem;
///// .... later ....
for( ... idx ...)
{
delete * vector[idx]; // delete the object
delete vector[idx]; // delete the pointer
}
vector.resize(0);
モジュール B/C はベクター項目を として読み取りBase**
、それをキャッシュする場合は としてキャッシュしBase**
ます。を減らすとBase*
、ケースは Case1 に変換されます。
まず、ベクトルをポインターで完全に初期化する必要があることに注意してください。そのようにする必要はありません。その場で行うことができますが、両方の行で「逆参照に注意してください」、 vector[nth] に適切に割り当てられたものがあることを絶対に確認する必要がありますpointer-to-Base*
(つまり、 Base** 、だからnew Base*
)。
ベクトルの要素は現在Base**
であるため、これらの B/C モジュールは -not ! をコピーした可能性がありBase**
ますBase*
。したがって、自然言語で言えば、モジュール B/Cは、オブジェクトの場所ではなく、ポインターの場所を記憶するようになりました。ポインターを別の場所を指すように変更すると、彼らはすぐにそれを見るでしょう。なぜなら、彼らは最初にポインターがある場所を見て、そこに新しいバージョンのポインターを見つけるからです。
そうすれば、カスケード更新は蒸発し、オブジェクトの所有権/削除も簡素化されますが、まだ存在します。しかし!また、新しいフラグメントが出現しました。追加のポインターを割り当てる必要があったため、ある時点でそれらを削除する必要もあります。
ケース 4:
vector<sharedptr<sharedptr<Base>>> vector;
for(int i = 0; i<10; ++i)
vector.push( new sharedptr<Base> );
///// .... later ....
(* vector[4] ).reset( new Bar() ); // note the dereference
///// .... later ....
vector.resize(0);
モジュール B/C はベクター項目を として読み取りsharedptr<sharedptr<Base>>
、それをキャッシュする場合は としてキャッシュしsharedptr<sharedptr<Base>>
ます。Base*
またはに削減するsharedptr<Base>
と、ケースはそれぞれ Case1 または Case2 に変換されます。
ここまでですべての違いが理解できたと思いますので、ここで何が起こるかについての詳細は繰り返しません。
免責事項: すべての例は単なる説明です。このコードはテストされていません。このコードは「完全」ではありません。たとえば、多くのエラー チェックが欠落しています。それらはメカニズムの骨組みを示すことだけを目的としています。タイプミスなどのためにコンパイルできないことさえあります。
最後に #1: もちろん、ポインターと共有ポインターを自由に混在させることができます。の代わりに、既に見たようにorまたはshared_ptr<shared_ptr<Base>>
を使用できます。初期化とクリーンアップにのみ影響します。置換更新の問題ではありません。shared_ptr<Base*>
shared_ptr<Base>*
Base**
最後の言葉 #2:参照もあることに注意してください。モジュール B/C がBase*
またはshared_ptr<Base>
を参照によってBase**
キャプチャした場合、またはを導入しても意味がありませんshared_ptr<shared_ptr<Base>>.
。参照によるキャプチャーは、必要だったのと同じもう 1 つのレベルの間接化を既に導入しています。実際、参照 & は内部的には単なる生のポインターであるため&-to-Base*
、実際にはBase**
.
最後の言葉 #3:
すでに述べましたが、繰り返します.. 中心的な問題はshared_ptr
、データではなく、参照カウントに関して「共有」されていることです。したがって:
shared_ptr<Foo> first = new Foo(1);
shared_ptr<Foo> second = first;
shared_ptr<Foo> third = second;
// now first == Foo#1 \
// now second == Foo#1 | refcount = 3
// now third == Foo#1 /
third.reset( new Bar(2) );
// now first == Foo#1 \ refcount = 2
// now second == Foo#1 /
// now third == Bar#2 - refcount = 1
second.reset( new Asdf(3) );
// now first == Foo#1 - refcount = 1
// now second == Asdf#3 - refcount = 1
// now third == Bar#2 - refcount = 1
first.reset( third );
// now first == Bar#2 \
// now second == Asdf#3 - refcount = 1 | - refcount = 2
// now third == Bar#2 /
// and Foo#1 gets deleted