「enable_shared_from_this」の有用性は何ですか?
-
23-08-2019 - |
質問
走って向かいました enable_shared_from_this
Boost.Asio の例を読んでいる間、そしてドキュメントを読んだ後でも、これをどのように正しく使用するべきか迷っています。誰かがこのクラスを使用するのが理にかなっている場合の例や説明を教えてください。
解決
shared_ptr
ときにこれは、this
するための有効なthis
インスタンスを取得することができます。それがなければ、あなたはすでにメンバーとして1を持っていない限り、shared_ptr
にthis
を取得する方法はありません。 enable_shared_from_this のためのブーストドキュメントからこの例:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_from_this();
}
}
int main()
{
shared_ptr<Y> p(new Y);
shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
メソッドf()は、それがメンバインスタンスを有していなかったにもかかわらず、有効shared_ptr
を返します。あなたは、単にこれを行うことはできません。
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
これが「正しい」ものとは異なる参照カウントを持つことになり、そのうちの一つは、オブジェクトが削除されたときにダングリング参照を失い、保持終わる返されます。 共有ポインタ
enable_shared_from_this
はC ++ 11標準の一部となっています。また、そこからだけでなく、ブーストからそれを得ることができます。
他のヒント
弱いポインタの博士ドブスの記事から、私はこの例では、(ソースを理解することが容易だと思います.COM / CPP / 184402026
に):...このようなコードが正しく動作しません。
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
2つのshared_ptr
オブジェクトのどちらも他を知っているので、両方のは、それらが破壊されたときにリソースを解放しようとします。これは通常、問題につながります。
shared_ptr
オブジェクトを必要とする場合は、同様に、それだけでその場でオブジェクトを作成することはできません。
struct S
{
shared_ptr<S> dangerous()
{
return shared_ptr<S>(this); // don't do this!
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->dangerous();
return 0;
}
より微妙な形態であるが、このコードは、前の例と同様の問題があります。それが構築されると、shared_pt
rオブジェクトsp1
は、新たに割り当てられたリソースを所有しています。メンバ関数S::dangerous
内のコードは、そのshared_ptr
オブジェクトについて知らないので、それは戻りshared_ptr
オブジェクトはsp1
区別されます。 shared_ptr
に新しいsp2
オブジェクトをコピーする助けにはなりません。 sp2
がスコープの外に出たとき、それがリソースを解放し、sp1
がスコープの外に出るときは、再度リソースを解放します。
この問題を回避する方法は、クラステンプレートenable_shared_from_this
を使用することです。テンプレートは、管理対象リソースを定義するクラスの名前である1つのテンプレート型引数を取ります。そのクラスは、今度は、テンプレートから公に導き出されなければなりません。このような:
struct S : enable_shared_from_this<S>
{
shared_ptr<S> not_dangerous()
{
return shared_from_this();
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->not_dangerous();
return 0;
}
あなたがこれを行うと、あなたはshared_from_this
呼び出しているオブジェクトがshared_ptr
オブジェクトによって所有されなければならないことに注意してください。これは動作しません。
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
これが基本的な観点からの私の説明です(一番上の回答は私にはピンと来ませんでした)。※これはVisual Studio 2012に付属するshared_ptrとenable_shared_from_thisのソースを調査した結果であることに注意してください。おそらく他のコンパイラは、enable_shared_from_this を別の方法で実装している可能性があります...*
enable_shared_from_this<T>
プライベートを追加します weak_ptr<T>
インスタンスへ T
'を保持する1 つの真の参照カウント' の例としては T
.
したがって、最初に作成するときは、 shared_ptr<T>
新しい T* に追加すると、その T* の内部weak_ptr が refcount 1 で初期化されます。新しい shared_ptr
基本的にこれに戻ります weak_ptr
.
T
その後、そのメソッド内で以下を呼び出すことができます shared_from_this
のインスタンスを取得するには shared_ptr<T>
それ 内部的に保存されている同じ参照カウントに戻ります. 。こうすることで、常に 1 つの場所が確保されます。 T*
の ref-count は複数を持つのではなく保存されます。 shared_ptr
インスタンスはお互いのことを知らず、それぞれが自分たちを自分だと思っています。 shared_ptr
それは参照カウントを担当します T
そして、ref-count がゼロに達したときにそれを削除します。
ブーストを使用していることに注意してください:: intrusive_ptrがこの問題に悩まされません。 これは、多くの場合、この問題を回避するために、より便利な方法です。
これは、C ++ 11以降ではまったく同じだ:それはthis
はあなたに生のポインタを与えますので、共有ポインタとしてthis
戻す機能を有効にすることです。
他の言葉で、それはあなたがこのようなコードをオンにすることができます。
class Node {
public:
Node* getParent const() {
if (m_parent) {
return m_parent;
} else {
return this;
}
}
private:
Node * m_parent = nullptr;
};
このへ
class Node : std::enable_shared_from_this<Node> {
public:
std::shared_ptr<Node> getParent const() {
std::shared_ptr<Node> parent = m_parent.lock();
if (parent) {
return parent;
} else {
return shared_from_this();
}
}
private:
std::weak_ptr<Node> m_parent;
};
もう一つの方法は、weak_ptr<Y> m_stub
にclass Y
メンバーを追加することです。その後、書き込みます:
shared_ptr<Y> Y::f()
{
return m_stub.lock();
}
便利なあなたが(例えば、他の人のライブラリを拡張する)から派生しているクラスを変更することができないとき。例えば、メンバーを初期化することを忘れないでくださいm_stub = shared_ptr<Y>(this)
によって、そのでも、コンストラクタ時に有効です。
このような多くのスタブが継承階層に存在する場合、それはオブジェクトの破壊を防ぐことはできませんOKです。
の編集のように正しくユーザーnobarが指摘割り当てが終了すると、一時的な変数が破壊された場合、コードはYオブジェクトを破壊するであろう。したがって、私の答えは間違っています。