Smart pointers
There are two types of smart pointers. The unique pointer is a standard method which came from C++11 implementation that returns a pointer. A unique pointer does not to be shared by other parts of the code. To use any of smart pointer you have to include the memory header file into your program.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#include <iostream> //memory file holds the code #include <memory> //this is a method to get a pointer int* getPointer(int value){ int* p = new int{value}; return p; } //method of printing void printOut(int* p){ if(!p){ return; } std::cout << *p << std::endl ; } void myMethod(int x){ std::unique_ptr<int> p{getPointer(x)}; //we have an object that hold the pointer if (p == nullptr){ p.reset(new int{x}); //reset is the function which deletes //the existing pointer memory space and //initilizes again. } printOut(p.get()); //get returns actual pointer p.reset(new int); *p = 2; //* is overrided operator which refer //memory location of the pointer //like actual * operator. printOut(p.get()); } int main(){ myMethod(34); return EXIT_SUCCESS; } |
So you may mention that t is not necessary to work about delete operation. That’s why the unique pointer overcomes our problem about memory management.
But if you copy of a unique pointer, for instance, passing to another method as a parameter, causes a problem that it deletes actual pointer. So you can’t pass a unique pointer to another method directly. Of course std::move function which calls move constructor to help us call a function. But keep it in mind, if you call a move method you can’t reach the old value of actual pointer after return the function.
But there is a solution to it. Passing reference of pointer instates of itself.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <iostream> #include <memory> void printOut(std::unique_ptr<int> &p){ if(!p){ return; } std::cout << *p << std::endl ; } void myMethod(int x){ std::unique_ptr<int> p{new int{x}}; printOut(p); // it uses pointer itself std::cout << *p << std::endl ; // so we can reach after it } int main(){ myMethod(34); return EXIT_SUCCESS; |
On the other hand, the shared pointer is fine in many cases. Shared pointers hold an indicator counter and it increases it in every case of constructors and decreases it in each deconstruction calls. Shared pointer gives memory to the system back when there is no other copy in a complex operation.
So you don’t have to pass a pointer of the unique pointer to a method. At the same time, there is only one way, shared pointer, in the case of “has a” relation of two objects.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <iostream> #include <memory> void printOut(std::shared_ptr<int> p){ if(!p){ return; } std::cout << *p << std::endl ; } void myMethod(int x){ std::shared_ptr<int> p{new int{x}}; printOut(p); std::cout << *p << std::endl ; } int main(){ myMethod(34); return EXIT_SUCCESS; } |
Weak Pointer
Shared pointer has two value, pointer and counter. The weak pointer indicates a pointer for the counter. And the weak pointer has two methods.
Expired is the method which returns a boolean value. If the shared pointers counter is 0 it’s expired.
The lock is the other method which returns a shared pointer of the pointer and increases the counter of it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <iostream> #include <memory> class Printer{ std::weak_ptr<int> ptr; public: void setVal(std::weak_ptr<int> ptr){ this->ptr = ptr; } void doIt(){ if(ptr.expired()){ std::cout << "Expired " << std::endl; return; } auto sp = ptr.lock(); std::cout << "Value is : " << *sp << std::endl ; } }; int main(){ Printer p; std::shared_ptr<int> mypointer{new int}; p.setVal(mypointer); //lets set the value of 3; *mypointer = 11; //lets check p.doIt(); } |
In circular reference, shared pointer causes a memory leak. We also use the weak pointer to avoid that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#include <iostream> #include <memory> class A; class B; class A{ public: /* shared pointer coused memory leak because it is circular std::shared_ptr<B> b_ptr; */ //Avoid it std::weak_ptr<B> b_weak_ptr; A(){ std::cout << "Init A" << std::endl; } ~A(){ std::cout << "Distroy A" << std::endl; } }; class B{ public: /* shared pointer coused memory leak because it is circular std::shared_ptr<A> a_ptr; */ //Avoid it std::weak_ptr<A> a_weak_ptr; B(){ std::cout << "Init B" << std::endl; } ~B(){ std::cout << "Distroy B" << std::endl; } }; int main(){ std::shared_ptr<A> a{new A}; std::shared_ptr<B> b{new B}; /* //lets make circular referance a->b_ptr = b; b->a_ptr = a; // It couse memory leak */ //without memory leak a->b_weak_ptr = b; b->a_weak_ptr = a; } |