Yanıtlar:
Sahip olduğunuz her şey için geçerli bir shared_ptr
örnek this
almanızı sağlar this
. O olmadan, bir almanın yolu yoktur shared_ptr
için this
zaten bir üye olarak bir vardı sürece. Enable_shared_from_this için boost belgelerinden bu örnek :
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
}
Üye örneği olmasa bile yöntem f()
geçerli bir değer döndürür shared_ptr
. Bunu yapamayacağınızı unutmayın:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
Bunun döndürdüğü paylaşılan işaretçinin "uygun" olandan farklı bir referans sayısı olacaktır ve bunlardan biri nesne silindiğinde sarkan bir referansı kaybedip tutacaktır.
enable_shared_from_this
C ++ 11 standardının bir parçası haline gelmiştir. Ayrıca buradan ve takviyeden de alabilirsiniz.
std::shared_ptr
bir üzerinde yapıcı ham pointer eğer o devralır std::enable_shared_from_this
. Boost'un anlambiliminin bunu destekleyecek şekilde güncellenip güncellenmediğini bilmiyorum .
std::shared_ptr
Zaten başka bir tarafından yönetilen bir nesne için bir yapı oluşturmak std::shared_ptr
, dahili olarak saklanan zayıf başvuru danışmaz ve böylece tanımlanamayan davranış yol açacaktır." ( en.cppreference.com/w/cpp/memory/enable_shared_from_this )
shared_ptr<Y> q = p
?
std::make_shared<T>
.
Zayıf işaretçiler hakkındaki Dr Dobbs makalesinden, bu örneğin anlaşılması daha kolay olduğunu düşünüyorum (kaynak: http://drdobbs.com/cpp/184402026 ):
... bunun gibi kodlar düzgün çalışmaz:
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
İki shared_ptr
nesneden hiçbiri diğerini bilmez, bu yüzden her ikisi de yok edildiğinde kaynağı serbest bırakmaya çalışır. Bu genellikle sorunlara yol açar.
Benzer şekilde, bir üye işlevi shared_ptr
çağrıldığı nesnenin sahibi olan bir nesneye ihtiyaç duyuyorsa, yalnızca anında bir nesne oluşturamaz:
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;
}
Bu kod, daha ince bir formda olmasına rağmen, önceki örnekle aynı soruna sahiptir. İnşa edildiğinde, shared_pt
r nesnesi sp1
yeni tahsis edilen kaynağın sahibidir. Üye işlevinin içindeki kod S::dangerous
o shared_ptr
nesneyi bilmez , bu nedenle shared_ptr
döndürdüğü nesne birbirinden farklıdır sp1
. Yeni shared_ptr
nesneyi kopyalamak sp2
yardımcı olmaz; ne zaman sp2
kapsam dışına gider, bu kaynağı serbest ve ne zaman olacak sp1
kapsam dışına gider, yine kaynak yayınlayacak.
Bu sorunu önlemenin yolu sınıf şablonunu kullanmaktır enable_shared_from_this
. Şablon, yönetilen kaynağı tanımlayan sınıfın adı olan bir şablon türü bağımsız değişkeni alır. Bu sınıf, sırayla, şablondan alenen türetilmelidir; bunun gibi:
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;
}
Bunu yaptığınızda, çağırdığınız nesnenin shared_from_this
bir shared_ptr
nesneye ait olması gerektiğini unutmayın . Bu işe yaramaz:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
shared_ptr<S> sp1(new S);
kullanılması tercih edilebilir shared_ptr<S> sp1 = make_shared<S>();
, örneğin bkz. Stackoverflow.com/questions/18301511/…
shared_ptr<S> sp2 = p->not_dangerous();
çünkü burada tuzak ilk kez aramadan önce normal şekilde bir shared_ptr oluşturmak gerekir shared_from_this()
! Bu yanlış yapmak gerçekten kolay! C ++ 17 de önce olan UB çağırmak shared_from_this()
: tam olarak bir Shared_ptr Normal şekilde yaratılmıştır önce auto sptr = std::make_shared<S>();
veya shared_ptr<S> sptr(new S());
. Neyse ki C ++ 17'den itibaren bunu yapacağız.
S* s = new S(); shared_ptr<S> ptr = s->not_dangerous();
<- shared_from_this öğesini yalnızca önceden paylaşılan bir nesne üzerinde, yani std :: shared_ptr <T> tarafından yönetilen bir nesne üzerinde çağırmaya izin verilir. Aksi takdirde, davranış tanımsızdır (C ++ 17'ye kadar) std :: bad_weak_ptr (varsayılan olarak oluşturulmuş bir zayıf_decin paylaşılan_ptr yapıcısı tarafından atılır) (C ++ 17'den beri). . Gerçek şu ki always_dangerous()
, çağrılmalı , çünkü zaten paylaşılıp paylaşılmadığı bilgisine ihtiyacınız var.
İşte benim açıklamam, somun ve cıvata perspektifinden (en iyi cevap benimle 'tıklamadı'). * Bunun Visual Studio 2012 ile birlikte gelen shared_ptr ve enable_shared_from_this kaynağının araştırılmasının sonucu olduğunu unutmayın. Belki de diğer derleyiciler enable_shared_from_this'i farklı şekilde uygular ... *
enable_shared_from_this<T>
weak_ptr<T>
örneğinin T
' bir gerçek referans sayısını ' içeren özel bir örnek ekler T
.
Böylece, shared_ptr<T>
yeni bir T * üzerine ilk oluşturduğunuzda , T * 'nin iç zayıf_ptr değeri 1 değerinde bir yeniden sayımla başlatılır. Yeni shared_ptr
temelde buna dayanır weak_ptr
.
T
daha sonra, metodlarını, arayabilir shared_from_this
bir örneği elde etmek için shared_ptr<T>
bu aynı iç saklanan referans sayısı üzerine sırt . Bu şekilde, birbirlerini bilmeyen T*
birden çok shared_ptr
örneğe sahip olmak yerine her zaman ref sayımının depolandığı tek bir yeriniz olur ve her biri shared_ptr
, ref sayımından T
ve ref sayımından silmekten sorumlu olduklarını düşünür. -count sıfıra ulaşır.
So, when you first create...
bunun bir gereklilik olmasıdır (zayıf_ptr dediğiniz gibi, nesne işaretçisini bir paylaşılan_ptr ctor'a geçirene kadar başlatılmaz!) Ve bu gereksinim, dikkatsiz. Aramadan önce shared_from_this
bir paylaşılan_ptr oluşturmazsanız UB alırsınız - aynı şekilde birden fazla paylaşılan_ptr oluşturursanız UB de alırsınız. Bir şekilde tam olarak bir kez shared_ptr oluşturduğunuzdan emin olmalısınız .
enable_shared_from_this
noktası elde edebilmek için olduğundan ile başlayacak kırılgandır shared_ptr<T>
bir mesafede T*
, ama gerçekte bir işaretçi olsun T* t
o önceden paylaşılan veya olmasın olmak hakkında bir şey varsaymak genellikle güvenli değildir ve yanlış tahminde bulunmak UB.
Bir boost :: intrusive_ptr kullanmanın bu sorundan muzdarip olmadığını unutmayın. Bu genellikle bu sorunu aşmanın daha kolay bir yoludur.
enable_shared_from_this
özellikle kabul eden bir API ile çalışmanıza olanak tanır shared_ptr<>
. Benim düşünceme göre, böyle bir API genellikle Yanlış Yapıyor (yığındaki daha yüksek bir şeyin belleğe sahip olmasına izin vermek daha iyi olduğu için), ancak böyle bir API ile çalışmak zorunda kalırsanız, bu iyi bir seçenektir.
C ++ 11 ve sonraki sürümlerde tamamen aynıdır: Size ham bir işaretçi verdiği this
için paylaşılan bir işaretçi olarak geri dönme özelliğini etkinleştirmektir this
.
başka bir deyişle, kodu böyle çevirmenize izin verir
class Node {
public:
Node* getParent const() {
if (m_parent) {
return m_parent;
} else {
return this;
}
}
private:
Node * m_parent = nullptr;
};
bunun içine:
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
. Durumun doğru olduğundan emin olmak için arayüzü değiştirmek isteyebilirsiniz.
std::shared_ptr<Node> getParent const()
normalde NodePtr getParent const()
bunun yerine ortaya koyarım. Kesinlikle dahili ham işaretçiye erişmeniz gerekiyorsa (en iyi örnek: bir C kütüphanesi ile uğraşmak), bunun std::shared_ptr<T>::get
için bahsetmekten nefret ediyorum, çünkü bu ham işaretçi erişimcisinin yanlış nedenle çok fazla kullandım.
Diğer bir yolu bir eklemektir weak_ptr<Y> m_stub
içine üyesi class Y
. Sonra yaz:
shared_ptr<Y> Y::f()
{
return m_stub.lock();
}
Türettiğiniz sınıfı değiştiremediğinizde kullanışlıdır (örneğin, başkalarının kitaplığını genişletme). Üyeyi başlatmayı unutmayın, örneğin m_stub = shared_ptr<Y>(this)
, bir kurucu sırasında bile geçerlidir.
Kalıtım hiyerarşisinde bunun gibi daha fazla taslak varsa, nesnenin yok edilmesini engellemez.
Düzenleme: Doğru kullanıcı nobar tarafından işaret gibi, atama bittiğinde ve geçici değişkenler yok edildiğinde kod Y nesnesini yok. Bu nedenle cevabım yanlış.
shared_ptr<>
amacınız pointee silmeyen bir tane üretmekse, bu aşırıya kaçmaktır. Hiçbir şey almayan ve yapmayan tek işlevli bir nesnenin return shared_ptr<Y>(this, no_op_deleter);
nerede no_op_deleter
olduğunu söyleyebilirsiniz Y*
.
m_stub = shared_ptr<Y>(this)
bundan geçici bir shared_ptr oluşturacak ve derhal imha edecektir. Bu ifade sona erdiğinde, this
silinecek ve sonraki tüm referanslar sarkacak.
enable_shared_from_this
bir weak_ptr
(kendisi tarafından doldurulan) kendisini tutar , shared_ptr
aradığınızda olarak döndürülür shared_from_this
. Başka bir deyişle, enable_shared_from_this
zaten sağlayanı çoğaltıyorsunuz .