enable_shared_from_thisを最初の基本クラスにする必要がありますか?


8

私のクラスは複数のベースから継承していますが、そのうちの1つはですstd::enable_shared_from_this。一塁でなければならないのですか?

次のコード例を考えてみます。

struct A { ~A(); };
struct B { ~B(); };
struct C : A, B, std::enable_shared_from_this<C> {};

std::make_shared<C>(); 

実行する~A()~B()C住んでいたストレージがまだ存在していることを確認できますか?


1
なぜ破壊の順番が重要だと感じるのですか?のデストラクタstd::enable_shared_from_thisは何もしません。あなたの例では、私には[OK]を探します(あなたが巧妙な何かをしようとしていないと仮定~Aして~Bのように、ダウンキャストthisC*
イゴールTandetnik

1
@SMこれはアクセスの問題ではありません。私enable_shared_from_thisはアクセス可能な明確な基盤でなければならないことを知っています。私の例では、そうです。C構造体です。それは公に継承します。
フィリップ

1
はい。ただし、ベースアクセスは、継承元のことではなく、継承することによって決まります。必要に応じて、例を変更できます。それが基づいている実際のコードはとを使用classpublicます。struct例として、入力を避けるために選びました。
フィリップ

4
したがって、標準を打ち消す :「[util.smartptr.weak.dest] ~weak_ptr(); 効果:このweak_ptrオブジェクトを破棄しますが、格納されたポインタが指すオブジェクトには影響しません。」強調鉱山。
Igor Tandetnik

1
@Filipp保存されたオブジェクトの寿命は、最後のオブジェクトがshared_ptr死んだときに終了します。たとえweak_ptr制御ブロックが割り当て解除されないようにしても、それは重要ではないと思います。
HolyBlackCat

回答:


1

実行する~A()~B()C住んでいたストレージがまだ存在していることを確認できますか?

もちろん!独自のメモリ(メモリが常駐するメモリ)を解放しようとする基本クラスを使用するのは難しいでしょう。それが正式に合法かどうかさえわかりません。

実装ではこれは行われません。a shared_ptr<T>が破棄またはリセットされると、の共有所有権の参照カウント(RC)Tが(原子的に)減少します。デクリメントで0に達した場合、の破棄/削除Tが開始されます。

次に、weak-owners-or-T-existsの数は(アトミックに)減少しますが、Tもう存在しません。制御ブロックに関心を持つ最後のエンティティであるかどうかを知る必要があります。デクリメントの結果がゼロ以外の場合weak_ptr、コントロールブロックの所有権を共有している(1共有、または100%になる可能性がある)いくつかが存在し、それらが割り当て解除を担当しています。

どちらの方法でも、最後の共同所有者の場合、アトミックデクリメントはある時点でゼロ値になります。

ここにはスレッドはなく、非決定性もありません。明らかに最後weak_ptr<T>はの破壊中に破壊されましたC。(あなたの質問の不文筆の仮定は、他にweak_ptr<T>は何も守られなかったということです。)

破壊は常にその正確な順序で発生します。最もshared_ptr<T>一般的に派生したクラスの(潜在的に異なる)デストラクタが(潜在的に非仮想)を呼び出すことを(一般的に)知らないため、制御ブロックは破棄に使用されます。(制御ブロックは、共有カウントが0に達したときにメモリを割り当て解除しないことも知ってますmake_shared。)

実装間の実際的な違いは、メモリフェンスの細部と、一般的なケースでのアトミック操作の回避についてのみであるようです。


これが私が探していた答えです!ありがとうございました!重要なのは、生きているオブジェクトは実際には1つの暗黙的な弱い使用として数えられるということです。つまり、s がない場合でも-edさweak_countれたオブジェクトの場合は1です。最初のデクリメントのみを解放します。0になると、オブジェクト(制御ブロックではない)が破棄されます。次に、デクリメントされ、0の場合、制御ブロックは破棄され、解放されます。から継承するオブジェクトは、= 2で始まります。予想どおり、STL実装者による素晴らしいソリューションです。make_sharedweak_ptrshared_ptruse_count weak_countenable_shared_from_thisweak_count
フィリップ

ひたむきな一言:STLは標準テンプレートライブラリです。これは、歴史的なアーティファクト(HP STL、またはSGI STL)を除いて、非公式にのみ定義されています。それは、コンテナ、イテレータ、およびそれらに取り組んでいる「アルゴリズム」の要件に従う型についてです。STLはいくつかの非テンプレートクラス(f.ex. random_access_iterator_tag)を使用するため、厳密にテンプレートに限定されません。コンテナに関連するものをSTLの一部と呼ぶことに非公式の合意があります。tl; dr:std libのすべてのテンプレートがSTLの一部であるわけではなく、すべての非テンプレートがその外部にあるわけではありません。
curiousguy

5

〜A()および〜B()を実行しても、Cが住んでいたストレージがまだ存在していることを確認できますか?

いいえ、基本クラスの順序は関係ありません。enable_shared_from_thisを使用してもしなくてもかまいません。

Cオブジェクトは(しかしそれが起こる)破壊されたときに、~C()呼び出される前に双方~A()~B()ベースが作業をデストラクタする方法であるとして、。いずれかのベースデストラクタでCオブジェクトを「再構築」して、そのフィールドにアクセスしようとすると、それらのフィールドはすでに破棄されているため、未定義の動作が発生します。


私の質問には答えません。Cを「再構築」しようとしているenable_shared_from_thisところはどこにもありません。回答は、「ベースリストのどこにでも出現できる」のいずれかである必要があります。enable_shared_from_this「」または「It最初のベースである必要があり、他のどこかを継承するのはUB "、または"この動作は指定されていない、または実装の品質 "です。
フィリップ

@Filipp:答えは組み合わせです。それらはどこにでも出現する可能性があります。オブジェクトの一部が破棄された後(および基本クラスが破棄される前)、実装はオブジェクトの一部のメモリを解放できます。とにかく、オブジェクト全体が破棄された後にのみメモリを解放できるという要件はありません。
クリスドッド

-1

ベースCのオブジェクトcを作成し、ベースA、B、およびリファレンスカウンターをベースから継承して作成するとenable_shared_from_this<T>、最初に、一般的なベースとベースを含む、結果として得られるオブジェクト全体に最初にメモリが割り当てられますenable_shared_from_this<T>。オブジェクトは、最後の所有者(別名shared_ptr)が所有権を放棄するまで破棄されません。その時点で〜enable_shared ...、〜Bおよび〜Aは〜Cの後に実行されます。完全に割り当てられたメモリは、最後のデストラクタ〜Aが実行されるまで存在することが保証されています。〜Aの実行後、オブジェクトメモリ全体が一気に解放されます。だからあなたの質問に答えるには:

〜A()および〜B()を実行しても、Cが住んでいたストレージがまだ存在していることを確認できますか?

はい、ありますが、合法的にアクセスすることはできませんが、なぜ知っておく必要があるのでしょうか。どの問題を回避しようとしていますか?


あなたが書いたことは真実ですが、私の質問には答えません。もちろん、基本クラスのデストラクターは派生クラスの後に実行されます。私が求めているの実装かどうかshared_ptrweak_ptrおよびenable_shared_from_this場合でも、この安全を作るために周りに十分な長さのメモリを維持するために必要とされるenable_shared_from_this最初の塩基ではありません。
フィリップ

ああ。あなたの元の質問を見てください:あなたが達成しようとしている明確な「これ」(上記のコメントの「これを保存する」のように)はありません。現時点で理解している質問を反映するように私の回答を編集します。
Andreas_75

これを安全にする= enable_shared_from_this別の基本クラスの後に継承する。
フィリップ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.