Intelligent pointer about Boost and Loki

xiaoxiao2021-03-06  105

Technical Documentation (Document) Writing / Mavita Smart Pointer Standards: Boost vs. Loki October 2001, in April 2002, two C standard meetings were held on the US Washington and the Netherlands in the United States. . One of the contents of the meeting is a new C feature proposal - smart pointer - discussion. This article will introduce and analyze the two intelligent pointer schemes that may become C new standards, and the corresponding usage examples are given.

Keywords: smart pointer C boost loki

In the current standard C , there is only one intelligent pointer: std :: auto_ptr. The reason is not because Auto_Ptr is enough to cope with all relevant work - actually, Auto_PTR has a major defect, which is not used in the STL container - but because the current C standard is not pair. Intelligent pointer to conduct a comprehensive investigation. According to the C Standard Committee Member Herb Sutter, only one standard smart pointer is a "shy" thing: first, many useful things that the smart pointer can do is poor auto_ptr can't complete; secondly, In some cases, using Auto_PTR may cause problems, which cannot be used in the container in the container. In fact, many programmers have developed a variety of useful smart pointers, and some have existed before auto_ptr is set, but the problem is that they are not standard. In this case, the C Standards Committee considers the introduction of new smart pointers, which is naturally a matter. At present, there are two main intelligent pointer programs for the committee: Boost intelligent pointers and Loki smart pointers. The former is developed by the Boost organization initiated by the C Standard Board Working Group, which is developed by world-class C expert Andrei Alexandrescu and has been detailed in the book "Modern C Design" book. Below, let us take a look at the technical characteristics of these two programs.

First, the smart pointer scheme of the Boost smart pointer implements five intelligent pointer templates, each of which is used for different purposes. These five intelligent pointers are:

template class scoped_ptr; template class scoped_array; template class shared_ptr; template class shared_array; template class weak_ptr;

Their respective features will be described below, and the corresponding examples are given:

Scoped_ptr: It is intended to be used as a smart pointer to the automatic (stack) object. This template class stores pointers that point to dynamically assigned objects (via NEW allocation). The object to be directed will be deleted, or when the Scoped_PTR is destructure, or by explicitly call the reset method. Note that the template does not have "shared ownership" or "ownership transfer" semantics. At the same time, it is also unable to copy (noncopyable). Because of this, it is more secure than shared_ptr or std: auto_ptr when it is used for pointers that should not be copied. Like Auto_PTR, Scoped_PTR cannot be used in STL containers; to meet such requirements, you should use Shared_PTR. In addition, it cannot be used to store pointers that point to the array of dynamically allocated, such a situation, SCOPED_ARRAY. Here is a simple instance using scoped_ptr:

Class ctest {public: ctest (): m_id (0) {} ctest (int ID): m_id (id) {} ​​~ ctest () {std :: cout << "ID:" << m_id << "- DESTRUCTOR Is Being Called / N ";} void setId (int id) {m_id = id;} int getId () {return m_id;} void dosomething () {std :: cout <<" ID: "<< m_id <<" - doing something / n ";} private: int m_id;}; void main () {boost :: scoped_ptr PTEST (new ctest); ptest-> DOSMETHING ()

Its operation is: ID: 0 - doing Something ID: 0 - Destructor is Being Called (the definition of the CTEST class used by several examples is exactly the same, saving space, no longer listed - author)

Obviously, although we didn't call DELETE, PTEST still deleted the object it pointed correctly. It looks similar to the use of scoped_ptr and auto_ptr, but in fact, the ownership of the Scoped_PTR type pointer is not transferable, this is quite different from Auto_Ptr.

Scoped_Array: This template class is similar to scoped_ptr, but is intended to be used for arrays instead of individual objects. Std :: Vector can be used to replace Scoped_Array and far flexibly, but its efficiency is low. Boost :: Array can also be used to replace Scoped_Array when not using dynamic allocation.

Here is an example of using scoped_array:

Void main () {boost :: scoped_array PTest (New ctest [2]); PTEST [0] .SetId (0); PTEST [1] .SetId (1); PTEST [0] .dosomething (); PTest [1] .dosomething (); std :: cout << '/ n';}

Its operation is: ID: 0 - doing Something ID: 1 - doing Something ID: 1 - Destructor Is Being Called ID: 0 - Destructor Is Being CalledScoped_Array will be responsible for using delete [], not delete to delete the object it points to . Shared_ptr: It is intended to share ownership of the object being directed. Like scoped_ptr, it is pointed to objects to be deleted, but in different, this will happen when the last one points to its share_ptr destruction, or calling the reset method. Shared_ptr complies with the "CopyConstructible" and "Assignable" requirements of the C standard library, so it can be used in standard library containers. In addition, it also provides comparison operators, so you can work with the associated container of the standard library. Shared_ptr cannot store pointers that point to the array of dynamically assigned, such cases should be used with Shared_Array. The implementation of this template adopts a reference counting technology, so it cannot correctly process the cycle reference. You can use Weak_PTR to "break the loop". Shared_ptr can also be used in multi-threaded environments.

The following example demonstrates how to use Shared_PTR to std :: Vector:

TYPEDEF BOOST :: Shared_Ptr Testptr; Void Pt (Const Testptr & T) {std :: cout << "ID:" << t-> getId () << "/ t / t" << "Uses: << T. IUSE_COUNT () << '/ n';} void main () {std :: vector testvector; testptr ptest0; testvector.push_back (ptest0); testptr ptest1 New ctest (1)); test correspondor.push_back (ptest1); testptr ptest2 (new ctest (2)); testvector.push_back (ptest2); std :: for_each (testvector.begin (), testvector.end (), pt) ; Std :: cout << '/ n'; ptest0.reset (); ptest1.reset (); ptest2.reset (); std :: for_each (testvector.begin (), testvector.end (), pt); Std :: cout << '/ n'; testvector.clear (); std :: cout << '/ n'; std :: cout << "exiting ... / n";}

Its operation results are: ID: 0 Use Count: 2 ID: 1 Use Count: 2 ID: 2 Use Count: 2 ID: 0 Use Count: 1 ID: 1 Use Count: 1 ID: 2 Use Count: 1 ID: 0 - Destructor is Being Called ID: 1 - Destructor Is Being Called ID: 2 - Destructor Is Being Called EXITING ... "Use count" in operation results are "Use count" obtained by shared_ptr's Use_count () method, that is The number of Shared_PTR objects for the stored pointer. We can see that three CTEST objects are assigned through the New, and the corresponding shared_ptr object is placed in TestVector, three usage counts are 2; and after we use the reset () method to reset the PTEST0, PTEST1 and PTEST2, The use count of each shared_ptr object in the TestVector is 1. At this time, we call TestVector's CLEAR () method to clear it it contains the Shared_PTR object; because there is no Shared_PTR object to point to the three CTEST objects we previously assigned, these three objects are deleted and causing corresponding elements. The structure is called.

Shared_Array: This template class is similar to Shared_Ptr, but is intended to be used for array instead of a single object. The shared_ptr pointing to std :: Vector can be used to replace Scoped_Array, and far flexibly, but its efficiency is low.

Here are an example:

Void main () {boost :: Shared_Array PTEST1 (New Ctest [2]); PTEST1 [0] .SetId (0); PTEST1 [1] .SetId (1); std :: cout << "Use Count : "<< PTEST1.USE_COUNT () <<" / n / n "; boost :: shared_array PTEST2 (PTEST1); std :: cout <<" Uses: "<< ptest1.use_count () << "/ n / n"; ptest1.reset (); ptest2 [0] .dosomething (); ptest2 [1] .dosomething (); std :: cout << '/ n'; std :: cout << " Count: "<< ptest1.use_count () <<" / n / n ";}

The results are: use count: 1 Use Count: 2 ID: 0 - doing Something ID: 1 - DOING Something Use Count: 1 ID: 1 - Destructor Is Being Called ID: 0 - DESTRUCTOR Is Being Called This example, The arrays allocated through the New are deleted only after the PTEST1 and PTEST2 points to it are destroyed or reset.

Weak_ptr: This template class stores "Weak references" of "Objects managed by Shared_PTR". To access the object points to the Weak_PTR, you can use the Shared_Ptr constructor or make_shared function to convert Weak_PTR to Shared_PTR. This object will be deleted when the last shared_ptr of the managed object is destroyed, even if there is still WEAK_PTR to point to it. Unlike the original pointer, the last Shared_PTR will check if there is Weak_ptr to point to this object, if any, will set these Weak_PTR to empty. This does not occur, "Dangling Pointer" condition that may occur when using the original pointer, thereby obtaining a higher level of security. WEAK_PTR complies with the "CopyConstructible" and "Assignable" requirements of the C standard library, so it can be used in standard library containers. In addition, it also provides comparison operators, so you can work with the associated container of the standard library. Void main () {boost :: shared_ptr PTest (new ctest); boost :: weak_ptr PTEST2 (PTEST); if (Boost :: Shared_Ptr :: Make_Shared (PTest2)) PTEST3 -> DOSMETHING (); PTEST.RESET (); assert (ptest2.get () == null);}

Its operation results are: ID: 0 - doing Something ID: 0 - Destructor Is Being CalledMain Function The last assertion confirmed that the pointer stored in PTEST2 has been set to NULL.

Obviously, Boost's smart pointer solution will make us have such a question: If we need other types of smart pointers (such as supporting COM's smart pointer), do we mean that we must add smart pointer types in C , or adopted What is the non-standard implementation? Today, in today's development, Boost's "increased increase increase" is unsatisfactory. It is here that we see the key points of the Loki Smart Pointer below to introduce: General Intelligent Pointer Templates are implemented by policy-based design.

Second, the Loki smart pointer is in accordance with the interpretation of American traditional dictionary (double solution), Loki is "A Norse God Who Created Discord, especially among his fellow gods." (A created god of Scandinavia, especially Its similarity between them). For the trouble brought by it, the Loki smart pointer is really affordable; on the other hand, the elegance and power of its implementation (that is, it gives the developer The benefits of coming) are concerned, it is indeed "God". It has been said that the smart pointer scheme of Loki uses a strategy-based design. It is in order to make a strategy to mix and match each functional domain into independent. Let's take a look at the definition of Loki Smart Pointer Template SmartPtr:

template class OwnershipPolicy = RefCounted, class ConversionPolicy = DisallowConversion, template class CheckingPolicy = AssertCheck, template class StoragePolicy = DefaultSPStorage> class SmartPtr; we can see, in addition pointed SmartPtr Object type T, including such policies in template class SMARTPTR: OwnershipPolicy, ConversionPolicy, CheckingPolicy, StoragePolicy (Storage Policy). It is through this decomposition, so that SmartPTR has great flexibility. We can set a variety of different strategies to achieve different intelligent pointers. Let's introduce each of the strategies first:

OwnershipPolicy: Specify the ownership management policy, you can select: DeepCopy, Refcounted, RefCounted, refplace (COM reference count), complCount (COM reference count), complCounted, refplace, refplace DESTRUCTIVECOPY (destroyed replication), and Nocopy. ConversionPolicy: Specifies whether to allow implicit conversions to be directed. The implementation of the implementation with AllowConversion and DisallowConversion. CheckingPolicy: Defines an error check policy. You can use AssertCheck, AssertCheckstrict, RejectnullStatic, Rejectnull, Rejectnullstrict, and Nocheck. StoragePolicy: Defines how to store and access being directed to objects. Loki defined strategies are: DefaultSpStorage, ArrayStorage, LockedStorage, and HeapStorage.

In addition to the policy that Loki has defined, you can also define a policy. In fact, Loki's smart pointer template covers four basic Boost intelligence pointer types: scoped_ptr, scoped_array, shared_ptr, and shared_array; as for Weak_PTR, it is also possible to implement its equivalent by defining the corresponding strategy. Through the TypeDef template feature that is about to become C standard (C 0x), we can also use the LOKI's SmartPTR template to directly define the first four intelligent pointer types of Boost mentioned. For example, we can define this:

shared_ptr: template // typedef templates is not the standard typedef Loki :: SmartPtr shared_ptr; Here is an Loki "shared_ptr "Example:"

Typedef loki :: smartptr; void pt (const testptr & t) {std :: cout << "ID:" << t-> getId () << '/ n';}

Void main () {std :: vector testvector; testptr ptest0; testvector.push_back (ptest0); testptr ptest1 (new ctest (1)); TestVector.push_back (PTest1); std: : for_each (TestVector.egin (), TestVector.eGin (), TestVector.end (), PT); std :: cout << '/ n'; loki :: reset (ptest0, null); Loki :: RESET (PTEST1, NULL); STD :: for_each (TestVector.egin (), TestVector.end (), Pt); std :: cout << '/ n'; testvector.clear (); std :: cout << '/ n'; std :: Cout << "exiting ... / n";

Its operation results are: ID: 0 ID: 1 ID: 0 ID: 1 ID: 0 - DESTRUCTOR Is Being Called ID: 1 - Destructor Is Being Called EXITING ...

As mentioned earlier, you have to define the export of the SHARED_PTR function with Boost, in addition to the first template parameter, other parameters can be used in the above example, so we use "Typedef" directly in the example above. Loki :: smartptr TestPtr; "You can. Very simple!

In order to further illustrate the "strategy-based design method", let's take a more complicated example: Thread-Specific Storage, TSS; also known as thread local storage, Thread LOCAL, THREAD LOCAL Storage, TLS).

The so-called thread proprietary storage refers to a mechanism that allows for a logical global access point to access the thread with extra lock overhead for each access. For a simple example, in the C language, we can get the error code through errno variables; usually Errno is a normal global variable - in a single-threaded environment, there is no problem, but if it is a multi-threaded environment, This overall Errno variable will bring us trouble. TSS is an effective solution to solve this problem.

Obviously, the semantics of the smart pointer can be suitable for TSS. We can write a smart pointer that makes all access to objects to the object become proprietary - that is, each thread is actually a proprietary object, but from the procedure's appearance It is all access to the same object. With Loki :: SmartPtr, we can easily implement such a smart pointer: If the name indicated by its name, TSS is involved in storage problems, we can customize the StoragePolicy of Loki :: Smartptr, and other work can be Hand it to Loki :: smartptr to complete. In Win32 and POSIX PThreads library are provided for the thread-specific storage functions, which are pthread_key_create, pthread_setspecific, pthread_getspecific and pthread_key_delete (POSIX PThreads library), and TlsAlloc, TlsSetvalue, TlsGetvalue and TlsFree (Win32). For more information on these functions, see the relevant documentation.

The StoragePolicy for TSS implementation under MSVC 6.0 is given, and analyzed by a comment (this implementation uses the PTHREADS-WIN32 library, which is a PTHREADS implementation on Win32. Use Win32 thread proprietary function can also Realize similar StoragePolicy, but clenuphook () that is called when writing at thread exits, it requires a "trick". Specific method can refer to Boost's Thread_Specific_PTR implementation):

Namespace loki {// Implement the Loki Storage Policy of TSS. Adapted from Douglas C. Schmidt, Timothy H. Harrison // and Nat Pryce's papers "Thread-Specific Storage for C / C " in some code. // use "Loki VC 6.0 Port". Template Class Ts_spStorage {public: typedef t * storedtype; // The type of object is directed to the object. TypeDef t * pointertype; // Operator-> The type returned. TypeDef T & ReferenceType; // Operator * The type returned. Public: // constructor. Initialize the member variable. TS_SPStorage (): ONCE_ (0), Key_ (0), Keylock_ (NULL) {pthread_mutex_init (& keylock_, null);} // destructor. Release the previously obtained resources. ~ Ts_spstorage () {pthread_mutex_destroy (& keylock_); pthread_key_delete (key_);} // Returns the thread exclusive object. Pointertype Operator -> () const {return getPointee () // Returns a reference to the lead-to-objective object. Referencetype Operator * () {return * getPointee ();} // accessors. Get the thread's exclusive object. Friend Inline Pointertype GetImpl (Ts_spStorage & SP) {Return Sp.getPointee ();} // Get reference to the thread is accommodated. // This function is not implemented. But Nocheck needs it to work properly. Friend Inline consttype & getImplref (const ts_spstorage & sp) {return 0;} protected: // destroyed the stored data. Empty function. This work will be completed by cleanuphook () to exit at each thread. Void Destroy () {} // Gets the current thread proprietary data. Pointertype getPointee () {Pointertype TSS_DATA = NULL; // Use Double Check Lock Mode to avoid locking in the case other than initialization. // This is here, not the "proprietor" in the constructor, and assigns TS objects, because: (1) initially creating threads of TS objects (for example, main thread) is often not used. // Its thread (working thread), so the instance of assigning a TS object in the constructor is often not // because this instance can only be accessed in the main thread. (2) On some platforms, "proprietary //-key" is a limited resource, so the allocation is made to the TS object for the first time, and there is // to help save resources. // First check. IF (ONCE_ == 0) {// is locked. Ensure the serialization of access.

PTHREAD_MUTEX_LOCK (& Keylock_); // Double Inspection. IF (ONCE_ == 0) {// Create "Specific Key". PTHREAD_KEY_CREATE (& key_, & cleanuphook); // must be in the final appearance of the creation process, so that other threads from using it before "Specific Key" // is created. ONCE_ = 1;} // Unlock. Pthread_mutex_unlock (& ​​keylock_);} // Get data from the system's thread proprietary storage. Note that you don't need to lock it. TSS_DATA = (POINTERTYPE) pthread_getspecific (key_); // Check that this is the current thread for the first time. IF (TSS_DATA == NULL) {// Assign memory from the heap to TS objects. TSS_DATA = New T; // stores its pointer in the system's thread proprietary storage. Pthread_setspecific (key_, (void *) tss_data);} Return TSS_DATA;} private: // "Specific Key" for thread proprietary data. Pthread_key_t key_; // "First Enter" logo. Int OnCE_; // is used to avoid generating a sense of racing during initialization. PTHREAD_MUTEX_T Keylock_; // Sweep the hook function, release the memory assigned by the TS object. The call is called when exiting each thread. Static void cleanuphook (void * ptr) {// Here you must perform type conversion, the corresponding destructor will be called (if any) delete (POINTERTYPE) PTR;}}; // Used to simulate the structure of TypedEf Template . // See the "template typef" article of Herb Sutter. Struct TS_SPStorageWrapper {Template struct in {typedef ts_spstorage type;};

Let's take a look at an example. Let us first define:

Loki :: smartptr value;

Its meaning is: Define a smart pointer, which is pointing by it is Int, OwnershipPolicy is NOCOPY, CONVERSIONPOLICY is disallowconversion, CheckingPolicy is Nocheck (because the TS object storage method is limited, this is the only CHECKINGPOLICY. Reader with TS_SPStorage You can try to find a better solution), StoragePolicy is TS_SPSTORAGE. Then, write such a program: pthread_mutex_t io_mutex = null; // iostreams is not necessarily thread secure! Void * thread_proc (void * param) {Int id = (int) param; * value = 0; for (int i = 0 i <3; i ) {(* value) ; pthread_mutex_lock (& ​​IO_MUTEX); std :: cout << "thread" << id << ":" << * value << '/ n'; pthread_mutex_unlock & IO_MUTEX);} return null;}

Void Main (int Argc, char * argv []) {pthread_mutex_init (& IO_MUTEX, NULL); pthread_t id [3]; for (int i = 0; i <3; i ) pthread_create (& ID [i], 0, thread_proc, (void *) i); for (int i = 0; i <3; i ) pthread_join (ID [i], null); pthread_mutex_destroy (& IO_MUTEX);

Its operation is: Thread 0: 1 Thread 0: 2 Thread 1: 1 Thread 2: 1 Thread 1: 2 Thread 2: 2 Thread 1: 3 Thread 2: 3 Thread 0: 3

This we can see that although it seems to be the same * Value in each thread, it is actually accessible to the respective thread proprietary objects. And in addition to initialization, access to these objects does not require serialization. Because Loki :: SmartPtr made us a lot of work, TS_SPStorage is very simple. Interested readers can compare the implementation of Boost's Thread_Specific_PTR. A customization of the strategy of Loki :: smartptr, in theoretical number is unlimited, that is, through it we can have a five-flowers, "thousands of monster" smart pointers - a great Loki, isn't it? With such a template class, we don't need to add new smart pointers in standard C once a time.

Third, ending the words in the predominator Boost and Scandinavian god Loki's war, who will win, I am afraid. If you play an eccentricity of God's C Standards, you have to select Boost's smart pointer scheme, then in the next day, they will no longer be the C Standards Committee, but C intelligent pointer to the fire committee. Even if the god of Loki, Scandina will still stand on the C continent, bringing unity, order and harmony for his "people" - C programmers. Related Resources 1. Herb Sutter, The New C : Smart (ER) Pointers, see C User Journal Site: http://www.cuj.com/experts/2008/sutter.htm2. Boost document: http: // www. boost.org3. Andrei Alexandrescu, Modern C Design and Loki, see http://www.moderncppdesign.com or http://www.awprofessional.com/catalog/product.asp?product_id={4ED3E6F3-371F-4ADC-9810 -CC7B936164E3% 7D. 4. Douglas C. Schmidt, Timothy H. Harrison and Nat Pryce, Thread-Specific Storage for C / C , see http://www.cs.wustl.edu/~schmidt/ or http: //www.flyingd& # 111nkey .com / ace / (Chinese). 5. Herb SUTTER, TEMPLATETEDEF, see http://www.gotw.ca/gotw/079.htm. 6. PTHREADS-WIN32 library, see http://sources.redhat.com/pthreads-win32/.

转载请注明原文地址:https://www.9cbs.com/read-100479.html

New Post(0)