Porady: tworzenie wystąpień weak_ptr i korzystanie z nich
Czasami obiekt musi przechowywać sposób dostępu do podstawowego obiektu z obiektu shared_ptr bez zwiększania licznika odwołań.Typowo, ta sytuacja występuje kiedy istnieją cykliczne odwołania między wystąpieniami shared_ptr.
Najlepszy projekt powinien unikać wspólnej własności wskaźników, ilekroć jest to możliwe.Jednakże jeśli musisz współużytkować własność wystąpień shared_ptr, unikaj odwołań cyklicznych między nimi.Kiedy nie da się uniknąć cyklicznych odwołań, lub są z jakiegoś powodu preferowane,użyj weak_ptr aby nadać jednemu lub większej ilości właścicieli słabe odwołanie do innego shared_ptr.Za pomocą weak_ptr, można utworzyć shared_ptr, który dołącza do istniejącego zestawu wystąpień powiązanych, ale tylko wtedy, jeśli zasób pamięci podstawowej jest nadal ważny.weak_ptr sam nie uczestniczy w zliczaniu odwołań i dlatego nie zapobiega liczeniu odwołańdo zera.Można jednak użyć weak_ptr, by spróbować uzyskać nową kopię shared_ptr, z której zostało to zainicjowane.Jeśli pamięć już została usunięta, zostanie wyrzucony wyjątek bad_weak_ptr.Jeśli pamięć jest nadal ważna, nowy wspólny wskaźnik zwiększa liczbę odwołań i gwarantuje, że pamięć będzie obowiązywać tak długo, jak zmienna shared_ptr pozostaje w zakresie.
Przykład
Poniższy przykład kodu pokazuje przypadek, gdzie weak_ptr służy do zapewnienia prawidłowego usuwania obiektów, które mają zależności cykliczne.Analizując przykład założono, że został on utworzony po tym jak zostały rozważone alternatywne rozwiązania.Obiekty Controller reprezentują niektóre aspekty procesu maszynowego i działają niezależnie od siebie.Każdy kontroler musi być w stanie zbadać stan innych kontrolerów w dowolnym momencie, a każdy z nich zawiera prywatny vector<weak_ptr<Controller>> do tego celu.Każdy wektor zawiera odwołanie cykliczne, a zatem, weak_ptr wystąpienia są używane zamiast shared_ptr.
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class Controller
{
public:
int Num;
wstring Status;
vector<weak_ptr<Controller>> others;
explicit Controller(int i) : Num(i) , Status(L"On")
{
wcout << L"Creating Controller" << Num << endl;
}
~Controller()
{
wcout << L"Destroying Controller" << Num << endl;
}
// Demonstrates how to test whether the
// pointed-to memory still exists or not.
void CheckStatuses() const
{
for_each(others.begin(), others.end(), [] (weak_ptr<Controller> wp)
{
try
{
auto p = wp.lock();
wcout << L"Status of " << p->Num << " = " << p->Status << endl;
}
catch (bad_weak_ptr b)
{
wcout << L"Null object" << endl;
}
});
}
};
void RunTest()
{
vector<shared_ptr<Controller>> v;
v.push_back(shared_ptr<Controller>(new Controller(0)));
v.push_back(shared_ptr<Controller>(new Controller(1)));
v.push_back(shared_ptr<Controller>(new Controller(2)));
v.push_back(shared_ptr<Controller>(new Controller(3)));
v.push_back(shared_ptr<Controller>(new Controller(4)));
// Each controller depends on all others not being deleted.
// Give each controller a pointer to all the others.
for (int i = 0 ; i < v.size(); ++i)
{
for_each(v.begin(), v.end(), [v,i] (shared_ptr<Controller> p)
{
if(p->Num != i)
{
v[i]->others.push_back(weak_ptr<Controller>(p));
wcout << L"push_back to v[" << i << "]: " << p->Num << endl;
}
});
}
for_each(v.begin(), v.end(), [](shared_ptr<Controller>& p)
{
wcout << L"use_count = " << p.use_count() << endl;
p->CheckStatuses();
});
}
int main()
{
RunTest();
wcout << L"Press any key" << endl;
char ch;
cin.getline(&ch, 1);
}
W ramach eksperymentu, należy zmodyfikować wektor others na vector<shared_ptr<Controller>>, a następnie w danych wyjściowych, należy zauważyć, że destruktory nie są wywoływane gdy TestRun zwraca.