美文网首页
c++ smart pointer

c++ smart pointer

作者: auguszou | 来源:发表于2017-06-10 11:37 被阅读0次

    malloc/free

    malloc/free are management methods of memory provided by C/C++,
    prototype:

    void *malloc(size_t size);
    void free(void *ptr);
    void *calloc(size_t nmemb, size_t size);
    void *realloc(void *ptr, size_t size);
    

    defined in #include<stdlib.h>

    new/delete

    new/delete are operator provided by C++, so there is no need to include any headers.
    prototype:

    void *operator new[](size_t);
    void *operator delete[](void *);
    

    new
    there are two things will be done by new operator.

    1. allocate memory
    2. call one or multiple constructor methods for memory allocated

    delete
    there are two things will be done by delete operator.

    1. call one or multiple destructor methods for memory to be freed
    2. free memory

    note
    you must add a [] after delete operator if allocate memory by new with a []

    Obj *objects = new Obj[100];
    delete []objects; // ok 
    delete objects;  // error
    

    auto_ptr

    it's a frustrating things for C/C++ developer to manage memory manually, and it will make our program crashed or memory leaked sometimes. C++ std introduced a pointer named auto_ptr originated from Boost. auto_ptr was a class template available in the C++ Standard Library (declared in the <memory>
    header file) that provided some basic RAII features for C++ raw pointers. It has been replaced by the unique_ptr class (refereced to wikipedia).
    prototype:

    amespace std {
    
        template <class Y> struct auto_ptr_ref {};
    
        template <class X>
        class auto_ptr {
        public:
            typedef X element_type;
    
            // 20.4.5.1 construct/copy/destroy:
            explicit           auto_ptr(X* p =0) throw();
                               auto_ptr(auto_ptr&) throw();
            template <class Y> auto_ptr(auto_ptr<Y>&) throw();
    
            auto_ptr&                      operator=(auto_ptr&) throw();
            template <class Y> auto_ptr&   operator=(auto_ptr<Y>&) throw();
            auto_ptr&                      operator=(auto_ptr_ref<X>) throw();
    
            ~auto_ptr() throw();
    
            // 20.4.5.2 members:
            X&     operator*() const throw();
            X*     operator->() const throw();
            X*     get() const throw();
            X*     release() throw();
            void   reset(X* p =0) throw();
    
            // 20.4.5.3 conversions:
            auto_ptr(auto_ptr_ref<X>) throw();
            template <class Y> operator auto_ptr_ref<Y>() throw();
            template <class Y> operator auto_ptr<Y>() throw();
        };
    }```
     it's convenient to use.
    ```cpp
    #include <iostream>
    #include <memory>
    using namespace std;
     
    int main(int argc, char **argv)
    {
        int *i = new int;
        auto_ptr<int> x(i);
        auto_ptr<int> y;
        
        y = x;
        
        cout << x.get() << endl; // Print NULL
        cout << y.get() << endl; // Print non-NULL address i
    
        return 0;
    }
    

    From above code, the auto_ptr has semantics of strict ownership, meaning that the auto_ptr instance is the sole entity responsible for the object's lifetime. It's destructor will free the memory owned by auto_ptr when a object auto_ptr lifetime ends . If an auto_ptr is copied, the source loses the reference.
    there are three methods to construct a auto_ptr:

    //1. construct by a regular pointer
    int* p = new int(33);
    auto_ptr<int> api(p);
    
    //construct directly
    auto_ptr< int > api( new int( 33 ) );
    
    //2. copy construct
    auto_ptr< string > pstr_auto( new string( "Brontosaurus" ) );
    auto_ptr< string > pstr_auto2( pstr_auto ); 
    
    //3. assignment by auto_ptr existed
    auto_ptr< int > p1( new int( 1024 ) );
    auto_ptr< int > p2( new int( 2048 ) );
    p1 = p2; # this will delete 1024 pointed by p1, not auto_ptr point to 1024
    

    ** if test**
    we can not judge a auto_ptr by if directly like a regular pointer.

    auto_ptr<int> pInt(new int(3));  
    if( pInt.get() )  
        cout<< *pInt<< endl;  
      
    //error C2451: conditional expression of type 'std::auto_ptr<_Ty>' is illegal  
    if( pInt )  
        cout<< *pInt << endl;  
    

    note

    1. cannot point a pointer to object allocated statically.
    2. cannot point the same object by two auto_ptr at the same time.
    3. cannot point a pointer to a array allocated dynamically
    4. cannot store a auto_ptr in a std container.

    The C++11 standard made auto_ptr deprecated, replacing it with the unique_ptr class template.[2][3] auto_ptr was fully removed in C++17[4]. For shared ownership, the shared_ptr template class can be used. shared_ptr was defined in C++11 and is available in the Boost library.[5]

    unique_ptr

    C++11 introduces unique_ptr, defined in the header <memory>
    A unique_ptr is a container for a raw pointer, which the unique_ptr is said to own. A unique_ptrexplicitly prevents copying of its contained pointer (as would happen with normal assignment), but the std::move function can be used to transfer ownership of the contained pointer to another unique_ptr. A unique_ptr cannot be copied because its copy constructor and assignment operators are explicitly deleted.

    std::unique_ptr<int> p1(new int(5));
    std::unique_ptr<int> p2 = p1; //Compile error.
    std::unique_ptr<int> p3 = std::move(p1); //Transfers ownership. p3 now owns the memory and p1 is rendered invalid.
    
    p3.reset(); //Deletes the memory.
    p1.reset(); //Does nothing.
    

    Since C++14 one can use:

    auto u = std::make_unique<some_type>(constructor, parameters, here);
    auto p = std::make_unique<int>(); // construct a int
    

    shared_ptr

    C++11 introduces std::shared_ptr and std::weak_ptr, defined in the header <memory>[3].

    A shared_ptr is a container for a raw pointer. It maintains reference counting ownership of its contained pointer in cooperation with all copies of the shared_ptr. An object referenced by the contained raw pointer will be destroyed when and only when all copies of the shared_ptrhave been destroyed.

    std::shared_ptr<int> p1(new int(5));
    std::shared_ptr<int> p2 = p1; //Both now own the memory.
    
    p1.reset(); //Memory still exists, due to p2.
    p2.reset(); //Deletes the memory, since no one else owns the memory.
    

    C++11 introduced std::make_shared

    auto s = std::make_shared<some_type>(constructor, parameters, here);
    auto p = std::make_shared<int>
    

    shared_ptr provide use_count method

    #include"stdafx.h"  
    #include<iostream>  
    #include<memory>  
    #include<string>  
    using namespace std;  
      
    int main()  
    {  
        struct MyStruct  
        {  
            MyStruct(int ii) : i(ii) {cout<<"MyStruct() i="<< i << endl;}  
            ~MyStruct(){cout<<"~MyStruct()i="<< i << endl;;}  
            int i;  
        };  
      
        { 
            shared_ptr<MyStruct> spMyStruct(new MyStruct(10));   
      
            // shared_ptr provied a method to test if a shared is null  
            if(spMyStruct)  
            {  
                cout << "1. Use count = " << spMyStruct.use_count() << endl;  
              
                shared_ptr<MyStruct> spMyStruct2 = spMyStruct;  //assignment
                cout<< "2. Use count = " << spMyStruct2.use_count() << endl;  
      
                shared_ptr<MyStruct> spMyStruct3(spMyStruct2);  //copy  
                cout<< "3. Use count = " << spMyStruct3.use_count() << endl;  
      
                spMyStruct2.reset();  
                cout<< "4. After reset, use count = " << spMyStruct.use_count() << endl; 
            }  
            cout << "5. spMyStruct2 and spMystruct3 out of scope:" << endl;  
            cout << "6. use count = " << spMyStruct.use_count() << endl;  
      
            cout <<"7. spMyStruct out of scope:" << endl;  
        }  
      
        system("pause");  
        return 0;  
    }  
    

    ** type conversion**
    we should not use static_cast to execute type conversion, the pointer converted can not managed by shared_ptr correctly. there are some methods static_pointer_cast<T>(), const_pointer_cast<T>() and dynamic_pointer_cast<T>() to execute type conversion for shared_ptr.

    #include"stdafx.h"  
    #include<iostream>  
    #include<memory>  
    #include<string>  
    using namespace std;  
      
    class BaseClass  
    {  
    public:  
        BaseClass(){}  
        ~BaseClass(){}  
    public:  
        virtual void Func() { cout<<"BaseClass::Func()\n";}  
    };  
      
    class DeriveClass : public BaseClass  
    {  
    public:  
        DeriveClass(){}  
        ~DeriveClass(){}  
    public:  
        virtual void Func() override { cout << "DeriveClass::Func()\n";}  
    };  
      
    int main()  
    {  
        shared_ptr<BaseClass> spBase( new BaseClass() );  
        spBase->Func(); // output BaseClass::Func()  
      
        shared_ptr<DeriveClass> spDerive( new DeriveClass() );  
        spDerive->Func();// output DeriveClass::Func()  
      
        shared_ptr<BaseClass> spBase2 = dynamic_pointer_cast<BaseClass>(spDerive);  
        spBase2->Func();// output DeriveClass::Func()  
      
        system("pause");  
        return 0;  
    }
    

    weak_ptr

    A weak_ptr is a container for a raw pointer. It is created as a copy of a shared_ptr. The existence or destruction of weak_ptr copies of a shared_ptr have no effect on the shared_ptr or its other copies. After all copies of a shared_ptr have been destroyed, all weak_ptr copies become empty.

    std::shared_ptr<int> p1(new int(5));
    std::weak_ptr<int> wp1 = p1; //p1 owns the memory.
    
    {
      std::shared_ptr<int> p2 = wp1.lock(); //Now p1 and p2 own the memory.
      if(p2) // As p2 is initialized from a weak pointer, you have to check if the memory still exists!
      {
        //Do something with p2
      }
    } //p2 is destroyed. Memory is owned by p1.
    
    p1.reset(); //Memory is deleted.
    
    std::shared_ptr<int> p3 = wp1.lock(); //Memory is gone, so we get an empty shared_ptr.
    if(p3)
    {
      //Will not execute this.
    }
    

    note
    Because the implementation of shared_ptr uses reference counting, circular references are potentially a problem. A circular shared_ptr chain can be broken by changing the code so that one of the references is a weak_ptr. Multiple threads can safely simultaneously access different shared_ptr and weak_ptr objects that point to the same object.[5]

    The referenced object must be protected separately to ensure thread safety. shared_ptr and weak_ptr are based on versions used by the Boost libraries. C++ Technical Report 1 (TR1) first introduced them to the standard, as general utilities, but C++11 adds more functions, in line with the Boost version.

    相关文章

      网友评论

          本文标题:c++ smart pointer

          本文链接:https://www.haomeiwen.com/subject/gyxxqxtx.html