c++ smart pointer

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

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
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  struct auto_ptr_ref {};

    template 
    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  auto_ptr(auto_ptr&) throw();

        auto_ptr&                      operator=(auto_ptr&) throw();
        template  auto_ptr&   operator=(auto_ptr&) throw();
        auto_ptr&                      operator=(auto_ptr_ref) 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) throw();
        template  operator auto_ptr_ref() throw();
        template  operator auto_ptr() throw();
    };
}```
 it's convenient to use.
```cpp
#include 
#include 
using namespace std;
 
int main(int argc, char **argv)
{
    int *i = new int;
    auto_ptr x(i);
    auto_ptr 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 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 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
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 p1(new int(5));
std::unique_ptr p2 = p1; //Compile error.
std::unique_ptr 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(constructor, parameters, here);
auto p = std::make_unique(); // construct a int

shared_ptr

C++11 introduces std::shared_ptr and std::weak_ptr, defined in the header [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 p1(new int(5));
std::shared_ptr 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(constructor, parameters, here);
auto p = std::make_shared

shared_ptr provide use_count method

#include"stdafx.h"  
#include  
#include  
#include  
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 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 spMyStruct2 = spMyStruct;  //assignment
            cout<< "2. Use count = " << spMyStruct2.use_count() << endl;  
  
            shared_ptr 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(), const_pointer_cast() and dynamic_pointer_cast() to execute type conversion for shared_ptr.

#include"stdafx.h"  
#include  
#include  
#include  
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 spBase( new BaseClass() );  
    spBase->Func(); // output BaseClass::Func()  
  
    shared_ptr spDerive( new DeriveClass() );  
    spDerive->Func();// output DeriveClass::Func()  
  
    shared_ptr spBase2 = dynamic_pointer_cast(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 p1(new int(5));
std::weak_ptr wp1 = p1; //p1 owns the memory.

{
  std::shared_ptr 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 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)