Let's start by explaining what smart pointers are.
A smart pointer is a RAII (the initialization of a pointer that executes a resource in the object's constructor) and is released in a destructor (delete pointer): This technique calls it RAII (Resource acquisition is Initialization: Resource acquisition is initialized)to manage resources.
The essence of the idea is that the lifetime of the heap object is managed by a stack object (smart pointer). That is, when new is a heap object, it is immediately taken over with a smart pointer , which is initialized in the constructor (pointing to the heap object with a pointer), and the destructor calls delete to free the heap object. because the smart pointer itself is a stack object, its scope ends when the destructor is called automatically, which calls delete to release the heap object.
In this way, the heap object is managed by a smart pointer (stack object), which is not prone to memory leaks or wild pointers.
Before explaining the smart pointers to the boost library, let's start with the auto_ptr in STL, which has a number of limitations based on the terms of the effective STL:
1. Auto_ptr cannot share ownership
2. Auto_ptr cannot point to an array
3. Auto_ptr cannot be a member of a container
4. Auto_ptr cannot be initialized by an assignment operation
Std::auto_ptr<int> p (new int (42)); Ok
std::auto_ptr<int> P=new Int (42); Error
This is because the auto_ptr constructor is defined as the explicit
5. Do not put auto_ptr into the container
because there are too many restrictions and inconvenient to use, I recommend using the Boost library's smart pointers (the Boost library can be downloaded with a simple configuration to use in VS):
(a) scoped_ptr<t>
#include <boost/scoped_ptr.hpp> #include <iostream>using namespace Std;class x{public: X () { cout << "X ..." << Endl; } ~x () { cout << "~x ..." << Endl; }}; int main (void) { cout << "Entering main ..." << Endl; { boost::scoped_ptr<x> pp (new X); boost::scoped_ptr<x> p2 (PP); Error: Ownership cannot be transferred } cout << "Exiting main ..." << Endl; return 0;}
Operation Result:
Entering main ...
X..
~x ...
Exiting main ....
Scoped_ptr cannot be copied and assigned, but can be exchanged. The reason for not being able to copy and assign values is that both the assignment operator and the copy constructor are private.
Reset () can also release the heap object, which is implemented using the interchange.
void Reset (T *p = 0) //Never throws{ boost_assert (p = = 0 | | P! = px);//Catch Self-reset errors This_type ( p). Swap (*this);} void swap (Scoped_ptr &b) //Never throws{ T *tmp = b.px; b.px = px; px = tmp;}
typedef scoped_ptr<t> THIS_TYPE; When calling Pp.reset (), the Reset function constructs a temporary object whose members are px=0, swapping pp.px in the swap function with
(This_type) (p). PX, which is now the smart pointer pp.px = 0; Unbind
The temporary object takes over the native pointer (that is, ownership can be exchanged), the Reset function returns, leaves the function scope, the temporary object on the stack is destructor, and the destructor is called, and then the delete px;
(ii) shared_ptr<t>
#include <boost/shared_ptr.hpp> #include <iostream>using namespace Std;class x{public: X () { cout << "X ..." << Endl; } ~x () { cout << "~x ..." << Endl; }}; int main (void) { cout << "Entering main ..." << Endl; boost::shared_ptr<x> P1 (new X); cout << p1.use_count () << Endl; boost::shared_ptr<x> P2 = p1; Boost::shared_ptr<x> P3; P3 = p1; cout << p2.use_count () << Endl; P1.reset (); cout << p2.use_count () << Endl; P2.reset (); cout << "Exiting main ..." << Endl; return 0;}
Operation Result:
Entering main ...
X...
1
2
1
~x ...
Exiting main ...
shared_ptr is implemented using reference counting, when the count value is 0, it means that the native pointer does not have any pointers to manage it, and executes the delete.
The counter changes are simple: When a new shared_ptr is administered to the native pointer, the count value is +1, and a shared_ptr is reduced by a value of 1 when the native pointer is managed.
The above mentioned auto_ptr can not be placed in the vector , but shared_ptr is possible. This is because the vector 's push_back(const _ty: The parameter is const) cannot be modified (without special handling), but the type of operator shared_ptr overloaded is Const, so shared_ptr can be used in the vector of the STL container. In addition, since his self-increment is atomic and thread-safe, this is very important in multithreaded programs.
Boost::shared_ptr is not absolutely secure, and the following rules will make it safer for us to use boost::shared_ptr:
1. Avoid Direct Memory management operations on objects managed by shared_ptr to prevent the object from being re-released
2. shared_ptr does not automatically manage the memory of objects that are circular references (this is a common problem with various other reference counts for managing memory patterns).
3. Do not construct a temporary shared_ptr as a function parameter.
Can see http://www.boost.org/doc/libs/1_52_0/libs/smart_ptr/shared_ptr.htm
(c)weak_ptr<t> the introduction of weak_ptr<t> is mainly to solve the problem of shared_ptr Circular reference:
#include <boost/shared_ptr.hpp> #include <iostream>using namespace Std;class parent;class child;typedef boost::shared_ptr<parent> parent_ptr;typedef boost::shared_ptr<child> child_ptr;class Child{public: Child () { cout << "Child ..." << Endl; } ~child () { cout << "~child ..." << Endl; } Parent_ptr Parent_;}; Class Parent{public: parent () { cout << "Parent ..." << Endl; } ~parent () { cout << "~parent ..." << Endl; } Child_ptr Child_;}; int main (void) { parent_ptr parent (new parent); Child_ptr Child (new); Parent->child_ = child; Child->parent_ = parent; return 0;}
as an example of the above procedure, the run program can find that both the child and the Parent constructor are called once, but the destructor is not called. because the parent and child objects reference each other,
their reference count is finally 1 and cannot be automatically freed, and the two objects are no longer reachable. This causes a memory leak .
One way to solve a circular reference problem is to manually break the circular reference, as in return 0; Before adding a sentence of Parent->child_.reset (), the circular reference is broken. But manual intervention has violated our intention to use smart pointers.
Here we use weak_ptr for problem solving. Here no longer follow the source code, only a simple introduction to use, interested friends can follow their own.
Two commonly used function functions: expired () is used to detect whether the managed object has been disposed, and lock () is used to get a strong reference smart pointer to the object being managed.
For the example above, the problem of circular references can be resolved by replacing the members of the parent class as follows:
Class Parent{public: boost::weak_ptr<parent> child_;};
strong references and weak references :
Strong reference, the object cannot be freed as long as a reference exists
A weak reference does not increase the reference count of the object (in fact it does not increase the USE_COUNT_, it increases the weak_count_), but it can know if the object exists
When accessing the members of an object through weak_ptr, promote to shared_ptr
If present, promotion to shared_ptr (strong reference) succeeded
if not present, promotion failed
#include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> #include <boost/scoped_array.hpp># Include <boost/scoped_ptr.hpp> #include <iostream>using namespace Std;class x{public:x () {cout & lt;< "X ..." << Endl; } ~x () {cout << "~x ..." << Endl; } void Fun () {cout << ' fun ... ' << Endl; }};int Main (void) {boost::weak_ptr<x> p; Boost::shared_ptr<x> P3; {boost::shared_ptr<x> P2 (new X); cout << p2.use_count () << Endl; p = p2; cout << p2.use_count () << Endl; /*boost::shared_ptr<x> */p3 = P.lock (); cout << p3.use_count () << Endl; if (!P3) cout << "object is destroyed" << Endl; else P3->fun (); }/*boost::shared_ptr<x> P4 = P.lock (); if (!P4) cout<< "object is destroyed" <<endl;else P4->fun (); */if (p.expired ()) cout << "object is destroyed" << Endl; else cout << "object is alived" << Endl; return 0;}
X...
1
1
2
Fun ...
Object is alived
~x ...
As can be seen from the output, when p = p2; The use_count_ is not increased, so P2.use_count () returns 1, while the elevation from p to P3 increases
Use_count_, P3.use_count () returns 2, curly braces, p2 is refactored, Use_count_ is reduced to 1, end of program ends, P3 is destroyed,
Use_count_ reduced to 0,x was reconstructed.
Extensions:
(a) explicit
The explicit keyword is used to modify the class's constructor, the class of the constructor that is decorated, the corresponding implicit type conversion cannot occur, and the type conversion can only be performed in the way shown.
Circle a=1.23;
The following actions are actually performed:
Tmp=cirlce (1.23);
Circle A (TMP);
Tmp.~circle ();
The copy constructor is implicitly called, which is wrong when the constructor is prefixed with the explicit keyword.
can only be Circle A(1.23); (double parameter)
Circle C (A); (copy constructor)
(ii) const
Const constants are not completely immutable, for example:const int n=100;
p=&n;
*p=200;
It is possible to modify by pointers, which is a technique.
Detailed boost library smart pointers (shared_ptr && scoped_ptr && weak_ptr)