回答:
持っているものがすべてあるshared_ptr
場合にthis
、有効なインスタンスをに取得できますthis
。それがないと、すでにメンバーとして持っているのでなければ、shared_ptr
to を取得する方法はありません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);
}
}
これが返す共有ポインタは、「適切な」ものとは異なる参照カウントを持ち、オブジェクトが削除されると、そのうちの1つがぶら下がっている参照を失って保持することになります。
enable_shared_from_this
C ++ 11標準の一部となっています。ブーストだけでなく、そこからも取得できます。
std::shared_ptr
std::enable_shared_from_this
std::shared_ptr
別のオブジェクトによって既に管理されているオブジェクトのを作成してもstd::shared_ptr
、内部に格納されている弱い参照は参照されないため、未定義の動作が発生します。」(en.cppreference.com/w/cpp/memory/enable_shared_from_this)
shared_ptr<Y> q = p
?
std::make_shared<T>
です。
ウィークポインターに関するドブス博士の記事から、この例は理解しやすいと思います(ソース:http : //drdobbs.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
}
shared_ptr<S> sp2 = p->not_dangerous();
ここに落とし穴は、あなたがいることであるので、あなたが呼び出す前に、通常の方法のshared_ptrを作成する必要がありますshared_from_this()
最初の時間を!これは間違いやすいです。C ++ 17 より前は、1つのshared_ptrが通常の方法で作成される前に呼び出すのはUBですshared_from_this()
:auto sptr = std::make_shared<S>();
またはshared_ptr<S> sptr(new S());
。ありがたいことにC ++ 17以降では、そうするとスローされます。
S* s = new S(); shared_ptr<S> ptr = s->not_dangerous();
<- 以前に共有されたオブジェクト、つまりstd :: shared_ptr <T>によって管理されているオブジェクトでのみ、shared_from_thisを呼び出すことが許可されています。そうでない場合、動作は未定義です(C ++ 17まで)std :: bad_weak_ptrがスローされます(デフォルトで作成されたweak_thisからのshared_ptrコンストラクターによって)(C ++ 17以降)。。したがって、always_dangerous()
それがすでに共有されているかどうかの知識が必要なため、実際にはを呼び出す必要があります。
ここに私の説明があります。一概に言えません(上の答えは私と一緒に「クリック」しませんでした)。*これは、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
基本的にthisに戻りますweak_ptr
。
T
次いで、その方法では、呼び出すことができるshared_from_this
のインスタンスを取得するためにshared_ptr<T>
、その同一の内部に記憶された参照カウントに背中を。このように、あなたは常に1位持っているT*
の参照カウントが複数持つのではなく、保存されているshared_ptr
お互いを知らないインスタンスを、それぞれが、彼らがいると思うshared_ptr
参照カウントを担当しているT
とするとき、その参照を削除します-countがゼロに達した。
So, when you first create...
、それが要件である(つまり、weak_ptrは、オブジェクトポインターをshared_ptr ctorに渡すまで初期化されない!)ためであり、この要件は、注意しないでください。呼び出す前にshared_ptrを作成しないshared_from_this
場合はUBが取得されます-同様に、複数のshared_ptrを作成する場合もUBが取得されます。あなたは何とかあなたがshared_ptrのを作成するために必要があり、正確に一度。
enable_shared_from_this
を取得できるようにすることがポイントなので、の全体的なアイデアは最初は脆弱ですが、実際には、ポインタを取得したときに、すでに共有されているかどうかを推測することは一般的に安全ではありません。間違った推測をするのはUBです。shared_ptr<T>
T*
T* t
これは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;
};
shared_ptr
。あなたはそれがそうであることを確認するためにインターフェースを変更したいかもしれません。
std::shared_ptr<Node> getParent const()
通常はNodePtr getParent const()
代わりに公開します。内部rawポインターへのアクセスが絶対に必要な場合(最良の例:Cライブラリーの処理)、それstd::shared_ptr<T>::get
について説明します。これは、このrawポインターアクセサーが間違った理由で何度も使用されたためです。
別の方法は、weak_ptr<Y> m_stub
メンバーをに追加することclass Y
です。次に書いてください:
shared_ptr<Y> Y::f()
{
return m_stub.lock();
}
派生元のクラスを変更できない場合に役立ちます(他の人のライブラリを拡張するなど)。たとえばによってメンバーを初期化することを忘れないでくださいm_stub = shared_ptr<Y>(this)
。それはコンストラクタの間でも有効です。
継承階層にこのようなスタブがさらに存在しても問題ありませんが、オブジェクトの破壊を防ぐことはできません。
編集:ユーザーnobarによって正しく指摘されているように、割り当てが完了して一時変数が破棄されると、コードはYオブジェクトを破棄します。したがって、私の答えは正しくありません。
shared_ptr<>
その指示先を削除しないaを生成することである場合、これはやりすぎです。単項関数オブジェクトreturn shared_ptr<Y>(this, no_op_deleter);
がどこにno_op_deleter
あり、Y*
何もしないところは簡単に言えます。
m_stub = shared_ptr<Y>(this)
これから一時的なshared_ptrを構築し、すぐに破棄します。このステートメントが終了すると、this
は削除され、後続のすべての参照がぶら下がります。
enable_shared_from_this
、それweak_ptr
自体の(ctorによって生成された)が保持されておりshared_ptr
、を呼び出したときにとして返されますshared_from_this
。言い換えれば、あなたはenable_shared_from_this
すでに提供しているものを複製しています。