Beyond the C ++ standard library: An Introduction to boost-library 1.4 shared_ptr

Source: Internet
Author: User
Shared_ptr

Header file: "Boost/shared_ptr.hpp"

Almost all programs with slightly complex points require some form of reference counting smart pointers. These smart pointers eliminate the need to write complex logic to control the lifetime of objects shared by two or more objects. When the reference billing is reduced to zero and no object needs to be shared, the object will be automatically destroyed. The reference counting smart pointer can be divided into two types: intrusive and non-intrusive. The former requires that the classes it manages provide clear functions or data members for managing reference counts. This means that when designing a class, you must anticipate that it will work with an plug-in reference counting smart pointer, or redesign it. Non-plug-in reference counting smart pointers have no requirements for the classes it manages. The reference counting smart pointer has the ownership of the memory related to its stored pointer. Without the help of smart pointers, there may be problems with object sharing. Someone must be responsible for deleting the shared memory. Who is responsible? When will it be deleted? Without smart pointers, you must increase the management of the lifetime outside the managed memory, which means that there is a stronger dependency between various owners. In other words, there is no reusability and complexity.

The Managed class may have some features so that it should be used together with the reference counting smart pointer. For example, its replication operations are expensive, or some of the items it represents must be shared by multiple instances. These features are worth sharing ownership. Another scenario is that shared resources do not have a clear owner. You can use the reference counting smart pointer to share the ownership of resources between objects that need to access shared resources. The reference counting smart pointer also allows you to store the object pointer into the container of the standard library without the risk of leakage, especially when you are facing exceptions or removing elements from the container. If you put the pointer into a container, you can get the benefits of polymorphism and improve performance (if the replication cost is high ), you can also put the same object into multiple auxiliary containers for specific search.

After you decide to use the reference count smart pointer, should you choose plug-in or non-plug-in? Non-plug-in smart pointers are almost always better choices because of their versatility, no need to modify existing code, and flexibility. You can use a non-plug-in reference counting smart pointer for classes that you cannot or do not want to modify. A common method for modifying a class to use the plug-in to reference the counting smart pointer is to derive from a reference counting base class. Such modifications may be more expensive than you think. At least, it increases correlation and reduces reusability.[6] It also increases the object size, which may limit its availability in some specific environments.[7]

[6] Consider the need to use more than two reference counting smart pointers for the same type. If both are plug-ins, two different base classes may be incompatible and will be wasted. If one of them is plug-in, the use of non-plug-in smart pointers can make the additional burden of the base class zero.

[7] On the other hand, non-plug-in smart pointers require additional storage for smart pointers themselves.

Shared_ptrYou can use one bare pointer and the otherShared_ptr,STD: auto_ptrOrBoost: weak_ptrStructure. You can also pass the second parameterShared_ptrIt is called the er ). The deleteer will be called later to release shared resources. This is not used to manage thoseNewAllocation is not used either.DeleteReleased resources are very useful (you will see an example of creating a custom deleteer later ).Shared_ptrAfter being created, it can be used like a normal pointer. Except for one point, it cannot be explicitly deleted.

The following is a summary of shared_ptr. The most important members and common functions are listed.

Namespace boost {

Template <typename T> class shared_ptr {
Public:
Template <class Y> explicit shared_ptr (y * P );
Template <class y, Class D> shared_ptr (y * P, d );

~ Shared_ptr ();

Shared_ptr (const shared_ptr & R );
Template <class Y> explicit
Shared_ptr (const weak_ptr <Y> & R );
Template <class Y> explicit shared_ptr (STD: auto_ptr <Y> & R );

Shared_ptr & operator = (const shared_ptr & R );

Void reset ();

T & operator * () const;
T * operator-> () const;
T * Get () const;

Bool unique () const;
Long use_count () const;

Operator unspecified_bool_type () const; // The original value is unspecified-bool-type ().

Void swap (shared_ptr <t> & B );
};

Template <class T, Class U>
Shared_ptr <t> static_pointer_cast (const shared_ptr <u> & R );
}

Member Functions
template <class Y> explicit shared_ptr(Y* p);

This constructor obtains the given pointer.P. ParametersPMust be pointingYValid pointer. After the construction, the reference count is set to 1. The only exception thrown from this constructor isSTD: bad_alloc(Only in a rare case, that is, the free space required to reference the counter cannot be obtained ).

template <class Y,class D> shared_ptr(Y* p,D d);

This constructor has two parameters. The first one isShared_ptrThe resource to be ownership of. The second one isShared_ptrAn object that is responsible for releasing resources when the resources are destroyed.D (P)To the object. ThereforePWhether the value is valid depends onD. If the reference counter cannot be allocated successfully,Shared_ptrThrowSTD: bad_alloc.

shared_ptr(const shared_ptr& r);

RResources are newly constructedShared_ptrShared, reference count plus one. This constructor will not throw an exception.

template <class Y> explicit shared_ptr(const weak_ptr<Y>& r);

Construct shared_ptr from a weak_ptr (described later in this chapter. This makesWeak_ptrIs thread-safe becauseWeak_ptrThe reference count of the shared resource of the parameter is automatically increased (Weak_ptrDoes not affect the reference count of shared resources ). IfWeak_ptrEmpty (R. use_count () = 0),Shared_ptrThrowBad_weak_ptr.

template <typename Y> shared_ptr(std::auto_ptr<Y>& r);

This ConstructorAuto_ptrObtainRTo save a copy of the pointer andAuto_ptrCallRelease. The reference count after construction is 1. WhileROf course it will become empty. If the reference counter cannot be allocated successfullySTD: bad_alloc.

~shared_ptr();

Shared_ptrThe Destructor reduces the reference count by one. If the count is zero, the saved pointer is deleted. To delete a pointer, callOperator DeleteOr, if a custom deleteobject for the delete operation is given, the stored pointer is called as a unique parameter. The Destructor does not throw an exception.

shared_ptr& operator=(const shared_ptr& r);  

Value assignment sharingRAnd stop sharing the original resources. The value assignment operation does not throw an exception.

void reset();

ResetThe function is used to stop sharing ownership of the stored pointer. The reference count of the shared resource is reduced by one.

T& operator*() const;

This operator returns a reference to the object pointed to by the existing pointer. If the pointer is null, callOperator *Will cause undefined behavior. This operator does not throw an exception.

T* operator->() const;

This operator returns the saved pointer. This operator correspondsOperator *Make smart pointers look like normal pointers. This operator does not throw an exception.

T* get() const;

GetWhen the stored pointer may be null (Operator *AndOperator->Will lead to undefined behavior) The best way to get it. Note that you can also use implicit boolean type conversion to testShared_ptrWhether a valid pointer is included. This function does not throw an exception.

bool unique() const;

This function is available inShared_ptrIs the unique owner of the pointer it saves.TrueOtherwiseFalse.UniqueNo exception is thrown.

long use_count() const;      

Use_countThe reference count of the pointer returned by the function. It is particularly useful during debugging because it can get a snapshot of the reference count at the key points of program execution. Use it with caution, because in some possible scenariosShared_ptrIn implementation, calculating the reference count may be expensive or even unavailable. This function does not throw an exception.

operator unspecified-bool-type() const;

This isUnspecified-bool-typeType implicit conversion function, which can test a smart pointer in the Boolean context. IfShared_ptrA valid pointer is saved, and the returned value isTrueOtherwiseFalse. Note that the type returned by the conversion function is uncertain. Treat the return typeBoolUsing it may lead to some absurd operations, so the typical implementation adopts safe bool idiom,[8] It ensures that only applicable Boolean tests are available. This function does not throw an exception.

[8] invented by Peter DIMOV.

void swap(shared_ptr<T>& b);

This can easily exchange twoShared_ptr.SwapFunction switches the stored pointers (and their reference counts ). This function does not throw an exception.

Common functions
template <typename T,typename U>
shared_ptr<T> static_pointer_cast(const shared_ptr<U>& r);

To saveShared_ptrPointer execution inStatic_cast, We can take out the pointer and forcibly convert it, but we cannot save it to anotherShared_ptrNewShared_ptrIt is regarded as the first to manage these resources. The solution is to useStatic_pointer_cast. Use this function to ensure that the reference count of the specified object is correct.Static_pointer_castNo exception is thrown.

Usage

UseShared_ptrThe primary problem is to know the right time to delete resources shared by multiple customers. The following is an easy-to-understand example. There are two classes:AAndB, They shareIntInstance. UseBoost: shared_ptr, You must include"Boost/shared_ptr.hpp".

#include "boost/shared_ptr.hpp"
#include <cassert>

class A {
boost::shared_ptr<int> no_;
public:
A(boost::shared_ptr<int> no) : no_(no) {}
void value(int i) {
*no_=i;
}
};

class B {
boost::shared_ptr<int> no_;
public:
B(boost::shared_ptr<int> no) : no_(no) {}
int value() const {
return *no_;
}
};

int main() {
boost::shared_ptr<int> temp(new int(14));
A a(temp);
B b(temp);
a.value(28);
assert(b.value()==28);
}

ClassAAndBAShared_ptr <int>. CreateAAndBWhen,Shared_ptr tempThe constructor that is passed to them. This means there are threeShared_ptr:A,B, AndTemp, They all lead to the sameIntInstance. If we use pointers to share one,AAndBYou must be able to point this out at a certain time.IntTo be deleted. In this exampleMainWhen allShared_ptrWhen the scope is removed, the count will reach 0, and the last smart pointer will be responsible for deleting the sharedInt.

Review pimpl usage

The previous section shows how to useScoped_ptrIf the class used in this method cannot be copiedScoped_ptrIt works well when saving the Dynamic Allocation instance of pimpl. But this is not suitable for all types that want to benefit from pimpl usage (note, you can also useScoped_ptrBut must be manually copied to the constructor and value assignment operator ). For classes that can handle shared implementation details, useShared_ptr. When pimpl ownership is transferred toShared_ptr, Copying and assigning operations are free of charge. You can recall when usingScoped_ptrDuring pimpl class lifetime processing, copying encapsulation classes is not allowed becauseScoped_ptrIt cannot be copied. This means that to make these classes support copying and assignment, you must manually define the copy constructor and assignment operator. When usingShared_ptrWhen processing the pimpl class lifetime, you no longer need to define your own replication constructor. Note: At this time, the pimpl instance is shared by multiple objects of the class. Therefore, if the rule is that each pimpl instance can only be used by one instance of the class, you still need to write a copy constructor manually. The solution is as follows:Scoped_ptrWhat we see is very similar.Scoped_ptrChangedShared_ptr.

Shared_ptr and standard library container

It is sometimes troublesome to directly store objects in a container. Saving an object by value means that the user will get a copy of the elements in the container, which may cause performance problems for the types of expensive operations. In addition, some containers, especiallySTD: VectorWhen you add an element, all elements may be copied, which increases the performance. Finally, the semantics of passing values means no polymorphism. If you need to store polymorphism objects in the container and you don't want to cut them, you must use pointers. If you use bare pointers, it will be very complicated to maintain the integrity of elements. When deleting an element from a container, you must know whether the container user is still referencing the elements to be deleted. Do not worry that multiple users use the same element. These problems can be usedShared_ptr.

The following is an example of how to store shared pointers to a standard library container.

#include "boost/shared_ptr.hpp"
#include <vector>
#include <iostream>

class A {
public:
virtual void sing()=0;
protected:
virtual ~A() {};
};

class B : public A {
public:
virtual void sing() {
std::cout << "Do re mi fa so la";
}
};

boost::shared_ptr<A> createA() {
boost::shared_ptr<A> p(new B());
return p;
}

int main() {
typedef std::vector<boost::shared_ptr<A> > container_type;
typedef container_type::iterator iterator;

container_type container;
for (int i=0;i<10;++i) {
container.push_back(createA());
}

std::cout << "The choir is gathered: /n";
iterator end=container.end();
for (iterator it=container.begin();it!=end;++it) {
(*it)->sing();
}
}

There are two classes,AAndBEach has a virtual member function.Sing.BSlaveAAnd factory functions, as you can seeCreateaReturns a dynamically assignedBIs encapsulated inShared_ptr <A>. InMainContainsShared_ptr <A>OfSTD: VectorPut 10 elements, and call each elementSing. If we use a bare pointer as an element, those objects need to be manually deleted. In this example, deletion is automatic becauseVectorEveryShared_ptrWhenVectorDestroyed. All reference counters are changed to zero and all objects are deleted. Interestingly, even ifAThe Destructor is not declaredVirtual,Shared_ptrIt will also be called correctlyBDestructor!

The above example demonstrates a powerful technology that involvesAThe protected destructor. Because the FunctionCreateaThe returned result isShared_ptr <A>Therefore, it is impossibleShared_ptr: GetReturned pointer callDelete. This means that if you want to transmit a bare pointer to a function that requires a bare pointerShared_ptrIf the bare pointer is removed from the file, it will not be accidentally deleted. How can we allowShared_ptrWhat about deleting its object? This is because the actual type pointed to by the pointer isBAndBThe destructor of is not protected. This is a very useful methodShared_ptrTo add additional security.

Shared_ptr and other resources

Sometimes you will find thatShared_ptrUsed for a special type. It requires other cleanup operations instead of simpleDelete.Shared_ptrYou can use the custom delimiters to support this requirement. Those processing imagesFile *Resources of such operating system handles usually useFcloseThis operation is released. ToShared_ptrUsed inFile *Define a class to release corresponding resources.

class FileCloser {
public:
void operator()(FILE* file) {
std::cout << "The FileCloser has been called with a FILE*, "
"which will now be closed./n";
if (file!=0)
fclose(file);
}
};

This is a function object. We use it to ensure that it is called when resources are to be released.Fclose. UseFilecloserClass sample program.

Int main (){
STD: cout <
"Shared_ptr example with a custom deallocator./N ";
{
File * f = fopen ("test.txt", "R ");
If (F = 0 ){
STD: cout <"unable to open file/N ";
Throw "unable to open file ";
}

Boost: shared_ptr <File>
My_shared_file (F, filecloser ());

// Locate the file pointer
Fseek (my_shared_file.get (), 42, seek_set );
}
STD: cout <"by now, the file has been closed! /N ";
}

Note: When accessing resources, we needShared_ptrUse&*Usage,Get, OrGet_pointer. (Please note that it is best to use&*The other two options are not clear.) This example can be simpler. If we only need to call a single-Parameter Function When releasing resources, we do not need to create a custom Delete type. The preceding example can be rewritten as follows:

{
File * f = fopen ("test.txt", "R ");
If (F = 0 ){
STD: cout <"unable to open file/N ";
Throw file_exception ();
}

Boost: shared_ptr <File> my_shared_file (F, & fclose );

// Locate the file pointer
Fseek (& * my_shared_file, 42, seek_set );
}
STD: cout <"by now, the file * has been closed! /N ";

The custom delimiters are useful when processing resources that require special release programs. Because the deleteer is notShared_ptrType, so you do not need to know any information about the resources of the smart pointer (except how to use it !). For example, you can use the object pool. The custom deleteer simply returns the object to the pool. Alternatively, a singleton object should use a deleteer that does nothing.

Security of using custom delimiters

We have seen that using the protected destructor for the base class can help increase the usage.Shared_ptrClass security. Another way to achieve the same security level is to declare the Destructor as protected (or private) and use a custom deleteer to destroy objects. The custom deleteer must be the friend of the class to be deleted so that it can work. A good way to encapsulate this deleteer is to implement it as a private nested class, as shown in the following example:

#include "boost/shared_ptr.hpp"
#include <iostream>

class A {
class deleter {
public:
void operator()(A* p) {
delete p;
}
};
friend class deleter;
public:

virtual void sing() {
std::cout << "Lalalalalalalalalalala";
}

static boost::shared_ptr<A> createA() {
boost::shared_ptr<A> p(new A(),A::deleter());
return p;
}

protected:
virtual ~A() {};
};

int main() {
boost::shared_ptr<A> p=A::createA();
}

Note that common functions cannot be usedShared_ptr <A>Because the nested delimiters areAPrivate. Using this method, you cannot create a stack.AObject, it is impossibleAPointer callDelete.

Create shared_ptr from this

SometimesThisObtainShared_ptrThat is, you want your class to beShared_ptrYou need to convert "yourself"Shared_ptr. It seems impossible? Okay, the solution comes from another smart pointer we will discuss.Boost: weak_ptr.Weak_ptrYesShared_ptrAn observer; it just sits quietly and looks at them, but does not affect the reference count. By storingThisOfWeak_ptrAs a member of the class, you can obtainThisOfShared_ptr. So that you do not have to write code to saveThisOfWeak_ptrAnd thenWeak_ptrObtainShared_ptr, Boost. smart_ptr provides a helper class for this task, calledEnable_shared_from_this. As long as your class is derived from the publicEnable_shared_from_thisInThisOfShared_ptrUse the FunctionShared_from_thisThat's all. The example below demonstrates how to useEnable_shared_from_this:

#include "boost/shared_ptr.hpp"
#include "boost/enable_shared_from_this.hpp"

class A;

void do_stuff(boost::shared_ptr<A> p) {
...
}

class A : public boost::enable_shared_from_this<A> {
public:
void call_do_stuff() {
do_stuff(shared_from_this());
}
};

int main() {
boost::shared_ptr<A> p(new A());
p->call_do_stuff();
}

This example also demonstrates how to useShared_ptrManagementThis. ClassAThere is a member functionCall_do_stuffYou need to call a common function.Do_stuff, This Normal function requires a typeBoost: shared_ptr <A>. NowA: call_do_stuff,ThisIt's justAPointer, but becauseADerived fromEnable_shared_from_this, CallShared_from_thisWill return what we wantShared_ptr. InEnable_shared_from_thisMember FunctionsShared_from_thisInternal StorageWeak_ptrConvertedShared_ptrTo ensure that the object is not deleted.

Summary

The reference counting smart pointer is an important tool. BoostShared_ptrIt provides a robust and flexible solution that has been widely used in multiple environments. Objects to be shared among users are common and there is usually no way to notify users when to delete objects safely.Shared_ptrUsers do not need to know or use other objects of shared objects, and do not need to worry about releasing resources when there is no object reference. This is the most important smart pointer class for boost. You will see other smart pointers in boost. smart_ptr, but this one is definitely what you want most. By using a custom deleteer, almost all resource types can be storedShared_ptr. This makesShared_ptrIt becomes a common class for processing resource management, not just for processing Dynamic Allocation objects. Compared with the bare pointer,Shared_ptrThere will be a little extra space cost. I have not found any other solution because of the high cost. Do not create your own reference count smart pointer class. NoneShared_ptrSmart pointers are better.

UseShared_ptr:

  • When multiple users use the same object without an explicit owner

  • When you want to store pointers in the standard library container

  • When an object is to be transferred to the database or obtained from the database without explicit ownership

  • When you manage resources that require special cleanup methods[9]

    [9] customize the help of the deleteer.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.