Sprytne wskaźniki
Trochę tytułem wstępu
Inteligentny wskaźnik pozwala na zarządzanie dynamicznie przydzieloną pamięć zgodnie z wzorcem RAII (Resource Acquisition Is Initialization). Sprytne wskaźniki dostarczają wszystkie interfejsy, które posiadają zwykłe wskaźniki z kilkoma drobnymi wyjątkami. Podczas tworzenia szarządza pamięcią i zwalnia ją, gdy obiekt znajdzie się poza zasięgiem życia. W ten sposób, programista jest zwolniony z ręcznego zarządzania dynamicznie przydzieloną pamięć.
auto_ptr
class Test
{
public:
Test(int a = 0 ) : m_a(a)
{
}
~Test( )
{
cout<<"Calling destructor"<<endl;
}
public:
int m_a;
};
int main( )
{
std::auto_ptr<Test> p( new Test(5) );
cout<<p->m_a<<endl;
}
shared_ptr
int main( )
{
shared_ptr<int> sptr1( new int );
}
int main( )
{
shared_ptr<int> sptr1 = make_shared<int>(100);
}
shared_ptr
class Test
{
public:
Test(int a = 0 ) : m_a(a)
{
}
~Test( )
{
cout<<"Calling destructor"<<endl;
}
public:
int m_a;
};
int main( )
{
shared_ptr<Test> sptr1( new Test[5] );
}
shared_ptr
shared_ptr dostarcza operatory *, -> tak jak zwykłe wskaźniki. Oprócz tego dostarcza również takie metody jak:
- get( ) : aktualny zasób przechowywany przez wskaźnik,
- reset( ) : zwalnia zasób, jeśli to jest ostatni wskaźnik,
- unique: pozwala sprawdzić, czy zasób jest wskazywany tylko przez jeden wskaźnik.
shared_ptr
int main( )
{
shared_ptr<int> sptr1( new int ); // 1
shared_ptr<int> sptr2 = sptr1; // 2
shared_ptr<int> sptr3; //
sptr3 = sptr2; // 3
}
shared_ptr
int main( )
{
int* p = new int;
shared_ptr<int> sptr1( p);
shared_ptr<int> sptr2( p );
}
shared_ptr
class B;
class A
{
public:
A( ) : m_sptrB(nullptr) { };
~A( )
{
cout<<" A is destroyed"<<endl;
}
shared_ptr<B> m_sptrB;
};
class B
{
public:
B( ) : m_sptrA(nullptr) { };
~B( )
{
cout<<" B is destroyed"<<endl;
}
shared_ptr<A> m_sptrA;
};
int main( )
{
shared_ptr<B> sptrB( new B );
shared_ptr<A> sptrA( new A );
sptrB->m_sptrA = sptrA;
sptrA->m_sptrB = sptrB;
}
X2
X3
X1
sp
sp
sp
kontenery z obiektami shared_ptr
A
sp
sp
sp
B
shared_ptr
Błędne działanie, jeśli pamięć bloku jest powiązana z shared_ptrs należącym do innej grupy.
Wszystkie shared_ptrs dzielące tę samą liczbę odwołań należą do grupy.
Kolejny problem gdy tworzymy shared_ptr z nagiego wskaźnika. W powyższym kodzie tylko jeden wspólny wskaźnik jest tworzony przy użyciu p i wtedy kod działa poprawnie.
Jeśli przez pomyłkę, programista zwalnia wskaźnik p przed końcem życia, to jest błąd.
Cykliczna referencja: zasoby nie są zwolnione prawidłowo, jeżeli istnieje cykliczne odniesienie do wspólnych wskaźników
Aby rozwiązać odwołanie cykliczne, C ++ zapewnia inną inteligentną klasę wskaźnika o nazwie weak_ptr
shared_ptr
sp
sp
sp
sp
sp
sp
kontener z obiektami shared_ptr
poszczególne obiekty wskazują na siebie nawzajem
weak_ptr
class B;
class A
{
public:
A( ) : m_sptrB(nullptr) { };
~A( )
{
cout<<" A is destroyed"<<endl;
}
shared_ptr<B> m_sptrB;
};
class B
{
public:
B( ) : m_sptrA(nullptr) { };
~B( )
{
cout<<" B is destroyed"<<endl;
}
shared_ptr<A> m_sptrA;
};
int main( )
{
shared_ptr<B> sptrB( new B );
shared_ptr<A> sptrA( new A );
sptrB->m_sptrA = sptrA;
sptrA->m_sptrB = sptrB;
}
weak_ptr
sp
sp
sp
sp
sp
sp
kontener z obiektami shared_ptr
poszczególne obiekty wskazują na siebie nawzajem poprzez weak_ptr
weak_ptr
A weak pointer pozwala na współdzielenie zasobów, ale sam nie posiada ich. To oznacza, że weak_potr może dzielić zasób przechowywawny przez shared_ptr. Żeby stworzyć weak_ptr, jakiś inny sprytny wskaźnik musi już posiadać zasób, który będzie współdzielony.
sp2
sp1
sp3
wp1
wp2
managed object
Manager Object
wskaźnik:
shared count: 3
weak count: 2
shared_ptrs
weak_ptrs
weak_ptr
weak_ptr nie posiada typowego interfejsu dostarczanego przez wskaźniki, nie ma operatorów * i -> Ponieważ nie posiada wskazywanego zasobu, w ten sposób zabezpiecza przed ewentualnym błędnym użyciem. Jak zatem go używać?
Odpowedź, to stworzenie shared_ptr oprócz weak_ptr i użycie go. Wtedy liczniki ustawią się odpowiednio (co namniej na wartość 1 i nie nastąpi niespodziewane zwolnienie pamięci). Inaczej, zasób trzymany przez weak_ptr, gdy może zostać zwolniony, bo wszystkie shared_ptr, które na niego wskazują go zwolniły.
weak_ptr
int main( )
{
shared_ptr<Test> sptr( new Test );
weak_ptr<Test> wptr( sptr );
weak_ptr<Test> wptr1 = wptr;
}
weak_ptr
Jak sprawdzić, czy zasób jest przechowywany poprawnie?
- use_count( ) - zwraca licznik silnych referencji (ile shared_ptr się odwołuje do zasobu).
- expired( ) - czy zasób już jest nieaktulny.
weak_ptr
int main( )
{
shared_ptr<Test> sptr( new Test );
weak_ptr<Test> wptr( sptr );
shared_ptr<Test> sptr2 = wptr.lock( );
}
weak_ptr
Stworzenie shared_ptr z weak_ptr zwiększa licznik silnych referencji.
weak_ptr
class B;
class A
{
public:
A( ) : m_a(5) { };
~A( ){cout<<" A is destroyed"<<endl;}
void PrintSpB( );
weak_ptr<B> m_sptrB;
int m_a;
};
class B
{
public:
B( ) : m_b(10) { };
~B( ){cout<<" B is destroyed"<<endl;}
weak_ptr<A> m_sptrA;
int m_b;
};
void A::PrintSpB( )
{
if( !m_sptrB.expired()){
cout<< m_sptrB.lock( )->m_b<<endl;
}
}
int main( ){
shared_ptr<B> sptrB( new B );
shared_ptr<A> sptrA( new A );
sptrB->m_sptrA = sptrA;
sptrA->m_sptrB = sptrB;
sptrA->PrintSpB( );
}
Unique_ptr
Cechą unique_ptr jest wyłącznosć dostępu do zasobów. W dowolnym momencie, okreslony zasób może być w posiadaniu tylko jednej instancji unique_ptr. Kiedy unique_ptr kończy się czas życia, przechowywany przez niego zasób jest zwalniany. Jeżeli zasób został nadpisany przez inny, to poprzedni jest zwalniany. Wynika z tego, że mamy gwarancję zwolnienia przechowywanego zasobu.
Unique_ptr
#include <memory>
{
std::unique_ptr<int> uptr( new int );
}
unique_ptr jest tworzony w taki sam sposób jak shared_ptr z jednym wyjątkiem. Posiada dodatkowe metody pozwalające na obsługę tablicy obiektów.
Unique_ptr
#include <memory>
{
unique_ptr<int[ ]> uptr( new int[5] );
}
Klasa unique_ptr dostarcza specjalizacji dla tablicy elementów, który wywołuje delete[ ] zamiast delete w momencie kiedy kończy się zakres życia instancji sprytnego wskaźnika.
Unique_ptr
Interfejs unique_ptr jest bardzo podobny do obsługi zwykłego wskaźnika za wyjątkiem operacji arytmetycznych, które nie są dozwolone.
unique_ptr dostarcza funkcji release(), która rezygnuje z prawa własności do zasobu. Różnica między release() i reset( ) polega na tym, że release nie wywołuje delete i nie niszczy przydzielonego zasobu, natomiast reset już tak.
sprytne wskaźniki
By pedzimaz
sprytne wskaźniki
Omówienie podstawowych klas sprytnych wskaźników z biblioteki standardowej: shared_ptr, weak_ptr, unique_ptr.
- 861