std::weak_ptr
std::weak_ptr
- std::weak_ptr is'nt a standalone smart pointer. It's an augmentation of std::shared_ptr.
- It do not contribute to the reference count of the managed object they refer to.
- By using just the raw pointer, it is impossible to know if the referenced data has been deallocated or not.
- std::weak_ptr provides a "expired" API to check if the shared_ptr it was pointing to is expired.
In a nutshell -
The weak_ptr class template stores a “weak reference” to an object that’s already managed by a shared_ptr. To access the object, a weak_ptr can be converted to a shared_ptr using the shared_ptr constructor or the member function lock. When the last shared_ptr to the object goes away and the object is deleted, the attempt to obtain a shared_ptr from the weak_ptr instances that refer to the deleted object will fail: the constructor will throw an exception of type boost::bad_weak_ptr, and weak_ptr::lock will return an empty shared_ptr.
auto sPtr = std::make_shared<Base>();
// The reference count of sPtr is 1.
std::weak_ptr<Base> wPtr(sPtr);
// wPtr points to same Base as sPtr. Reference count remains same
sPtr = nullptr;
// Referece count is 0 now, and the Base is destroyed, wPtr now dangles.
std::weak_ptr that dangles are said to be expired.
if(wPtr.expired())
- std::weak_ptr lacks dereferencing operations (*, ->), to point to the shared_ptr it was referring to.
There are two ways to get the shared_ptr from weak_ptr
- One form is std::weak_ptr::lock - which returns a std::shared_ptr , the std::shared_ptr is null if the std::weak_ptr is expired.
std::shared_ptr<Base> sPtr1 = wPtr.lock();
// If wPtr is expired, sPtr1 is null
- The other form is the std::shared_ptr constructor taking a std::weak_ptr as an argument. If passed std::weak_ptr has expired, an exception is thrown.
std::shared_ptr<Base> sPtr3(wPtr);
// If wPtr is expired, throw std::bad_weak_ptr
Usage of std::weak_ptr.
- To solve dangling pointer.
- To remove circular reference.
Solve dangling pointer.
// Check if the pointer is expired, then get assocaited pointer and print the contents.
Another example to check for the dangling pointer.
#include <iostream>
#include <memory>
int main()
{
// PROBLEM: ref will point to undefined data!
int* ptr = new int(10);
int* ref = ptr;
delete ptr;
// Check expired() or lock() to determine if pointer is valid
// empty definition
std::shared_ptr<int> sptr;
// takes ownership of pointer
sptr.reset(new int);
*sptr = 10;
// get pointer to data without taking ownership
std::weak_ptr<int> weak1 = sptr;
// deletes managed object, acquires new pointer
sptr.reset(new int);
*sptr = 5;
// get pointer to new data without taking ownership
std::weak_ptr<int> weak2 = sptr;
// weak1 is expired!
if(auto tmp = weak1.lock())
std::cout << *tmp << '\n';
else
std::cout << "weak1 is expired\n";
// weak2 points to new data (5)
if(auto tmp = weak2.lock())
std::cout << *tmp << '\n';
else
std::cout << "weak2 is expired\n";
}
/*
Output-
weak1 is expired
5
*/
Remove circular reference.
Remove circular reference.
Consider the below example -
class B;
class A
{
public:
A() : sPtrToB(nullptr) { };
~A()
{
cout<<"A destrcutor\n";
}
std::shared_ptr<B> sPtrToB;
};
class B
{
public:
B() : sPtrToA(nullptr) { };
~B()
{
cout<<"B desctructor\n"
}
std::shared_ptr<A> sPtrToA;
};
int main()
{
shared_ptr<A> sPtrA ( new A ); // Ref Count of sPtrA = 1
shared_ptr<B> sPtrB ( new B ); // Ref Count of sPtrB = 1
sPtrA->sPtrToB = sPtrB; // Ref Count of sPtrA = 2
sPtrB->sPtrToA = sPtrA; // Ref Count of sPtrB = 2
}
Ref count of sPtrA and sPtrB is 1.
The above code has cyclic reference. class A holds a shared pointer to B and class B holds a shared pointer to A. The resource associated with both sPtrToA and sPtrToB are not released.
It can be solved using having a std::weak_ptr as data member and as reference to another class.
class A
{
public:
int _a;
weak_ptr<B> sPtrToB;
void printB()
{
if(sPtrToB->expired())
{
cout<<"Expired....!!!\n"
}
else
{
cout<<sPtrToB->lock()->_b<<endl;;
}
}
};
class B
{
public:
int _b;
weak_ptr<A> sPtrToA;
void printA()
{
if(sPtrToA->expired())
{
cout<<"Expired....!!!\n"
}
else
{
cout<<sPtrToA->lock()->_a<<endl;;
}
}
};
Comments
Post a Comment