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<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.
- allocate memory
- call one or multiple
constructor
methods for memory allocated
delete
there are two things will be done by delete
operator.
- call one or multiple
destructor
methods for memory to be freed - 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
- cannot point a pointer to object allocated statically.
- cannot point the same object by two
auto_ptr
at the same time. - cannot point a pointer to a array allocated dynamically
- 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_ptr
explicitly 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.