1 智能指针的引入
有时候,我们new了,也delete了,但是还会出现问题。例如在new和delete之间调用了某个抛异常的函数,就有可能导致没有执行delete。
例如:fun2里面使用了new[],最后也使用了delete[],仍然会有内存泄露的问题。
void fun1()
{
throw int(11);
}
void fun2()
{
int* p = new int[10000];
fun1();
delete[] p;
}
int main()
{
try
{
fun2();
}
catch (int& e)
{
cout << "捕获" << endl;
}
system("pause");
return 0;
}
要解决上面的问题,有一种方法:就是如果发现delete之前发现某个函数抛出了异常,就在delete之前捕获这个异常,并且在catch语句里面进行资源的释放,并且可以再将这个异常重新抛出。
void fun2()
{
int* p = new int[10000];
try
{
fun1();
}
catch(int& e)
{
delete[] p;
cout << "重新抛出" << endl;
throw;
}
delete[] p;
}
但是这种方法写着比较繁琐。
4.智能指针就有上面的作用,能够自动的管理指针所指向的动态资源的释放。它不仅有着RAII的思想还能够像指针一样。(RAII:资源分配即初始化,即构造函数分配资源和初始化资源,在析构函数清理资源。像指针一样:能够解引用)。
5.智能指针实质上就是一个模板类,成员变量包含一个任意类型的指针,构造函数获得资源并初始化,析构函数清理资源。
注意:智能指针只能管理动态开辟的空间。
2 智能指针的发展史
当用一个智能指针拷贝构造另一个智能指针的时候,有可能会有浅拷贝的问题,这个空间会被释放多次,智能指针的发展就是围绕着指针拷贝问题而走。
2.1 auto_ptr
(1)C++98里面有一个智能指针auto_ptr,对于拷贝构造和赋值运算符重载,该智能指针采用管理权转移的方式(当一个指针拷贝构造另一个指针时,当前指针就将对空间的管理权交给拷贝的那个指针,当前指针就指向空);
(2)但是这种方式不符合指针的要求(可以允许多个指针指向同一块空间,将一个指针赋值给另一个指针的时候,就是需要让两个指针指向同一块空间,而auto_ptr只允许一块空间上只能有一个指针指向它),并且当管理权转移之后要想再访问之前的指针,就会出错,因为之前的指针已经为NULL,就会出现引用空指针的问题。
2.2 scoped_ptr/shared_ptr
因为auto_ptr有缺陷,但是C++标准里面从C++98到C++11之间没有出现新的智能指针能解决这个缺陷,所以在这段时间内,boost这个官方组织就增加了智能指针(scoped_ptr,shared_ptr,weak_ptr等)
(1)scoped_ptr采用防拷贝的方式(防拷贝就是不允许拷贝,拷贝就会出错;防拷贝的实现:将拷贝构造和赋值运算符重载只声明不实现,并且声明为私有);
(2)shared_ptr为共享指针,里面采用引用计数,当有shared_ptr指向同一块空间的时候就增加引用计数,当引用计数减为0的时候才释放该智能指针管理的那块空间。
(3)但是shared_ptr有一个缺点,就是会出现循环引用的问题(当一个shared_ptr(如sp1)管理的空间里面包含一个shared_ptr的指针(_next),另一个shared_ptr(如sp2)管理的空间里面也包含一个shared_ptr指针(_prev)时,当sp1->_next = sp2;sp2->_prev = sp1;此时就会使得sp1和sp2的引用计数都变为2,当出了这个作用域sp1和sp2的引用计数都会减为1,但是只有引用计数为0时才会释放管理的空间,就会使得sp1和sp2管理的空间没有释放。
(4)所以利用weak_ptr来解决循环引用的问题,weak_ptr叫弱指针,它主要是为了配合shared_ptr使用,用来解决循环引用的问题;
- 将会出现循环引用问题的指针用weak_ptr保存着,weak_ptr并不拥有这块空间,所以weak_ptr里面不增加shared_ptr的引用计数,也不会释放这块空间。(注意weak_ptr里面也有自己的引用计数)
(5)boost库里面还包含scoped_array和shared_array(这个适用于delete[]的场景)
2.3 C++11(unique_ptr和shared_ptr)
(1)C++11借鉴了boost库里面的智能指针(C++对应的智能指针位于头文件<memory>
里。
C++11里面的unique_ptr就是boost库里面的scoped_ptr(防拷贝);
C++11里面的shared_ptr就是boost里面的shared_ptr。
(2)C++11里面不包含类似于scoped_array和shared_array,而它采用定制删除器的方式管理空间的释放。
- 定制删除器就是自己指定采用何种方式释放该空间(delete/free或其它)(例如new出来的数据,就必须用delete,而new[ ]就需要delete[ ]等)。注意:boost库里面也包含定制删除器。
区别:
unique_ptr
不可复制 ,但支持转移。
scoped_ptr
既不可复制也不可移动 。 这是当想出作用域时,确保指针被删除的首选。
3 auto_ptr
1.源代码分析:
template<class _Ty>
class auto_ptr
{
public:
typedef auto_ptr<_Ty> _Myt;
typedef _Ty element_type;
//构造函数
explicit auto_ptr(_Ty *_Ptr = 0) _THROW0()
: _Myptr(_Ptr)
{}
//拷贝构造函数,release返回保存_Right指向空间的临时变量,则_Myptr管理_Right管理的空间,并将_Right置为空
auto_ptr(_Myt& _Right) _THROW0()
: _Myptr(_Right.release())
{}
//赋值运算符重载,_Right.release()将_Right的管理权转移给_Myptr,将_Right置为空。
//reset里面判断是不是自己给自己赋值,不是就释放_Myptr。
_Myt& operator=(_Myt& _Right) _THROW0()
{
reset(_Right.release());
return (*this);
}
//析构函数释放_myptr
~auto_ptr() _NOEXCEPT
{
delete _Myptr;
}
_Ty& operator*() const _THROW0()
{
#if _ITERATOR_DEBUG_LEVEL == 2
if (_Myptr == 0)
_DEBUG_ERROR("auto_ptr not dereferencable");
#endif
return (*get());
}
_Ty *operator->() const _THROW0()
{
#if _ITERATOR_DEBUG_LEVEL == 2
if (_Myptr == 0)
_DEBUG_ERROR("auto_ptr not dereferencable");
#endif
return (get());
}
//get返回原生指针
_Ty *get() const _THROW0()
{
return (_Myptr);
}
//release用于管理权的转移,将_myptr保存在tmp里面,将_mytmp的指针置为空,再返回tmp
_Ty *release() _THROW0()
{ // return wrapped pointer and give up ownership
_Ty *_Tmp = _Myptr;
_Myptr = 0;
return (_Tmp);
}
void reset(_Ty *_Ptr = 0)
{
if (_Ptr != _Myptr)
delete _Myptr;
_Myptr = _Ptr;
}
private:
_Ty *_Myptr; // the wrapped object pointer
};
2.使用:(必须包含头文件<memory>
)
将动态开辟的指针交给一个智能指针。
void Test_auto_ptr()
{
auto_ptr<int> ap1(new int(10));
cout << *ap1 << endl; //输出10
auto_ptr<int> ap2(ap1);
cout << *ap2 << endl; //输出10
cout << *ap1 << endl; //此时ap1为NULL,就是解引用空指针,程序崩溃
}
scoped_ptr
1.源代码:
template<class T>
class scoped_ptr
{
private:
T * px; //只含有一个成员变量T*的指针
//将拷贝构造与赋值运算符重载声明为私有,且不实现
scoped_ptr(scoped_ptr const &);
scoped_ptr & operator=(scoped_ptr const &);
typedef scoped_ptr<T> this_type;
void operator==( scoped_ptr const& ) const;
void operator!=( scoped_ptr const& ) const;
public:
typedef T element_type;
//构造函数
explicit scoped_ptr( T * p = 0 )
: px( p )
{
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
boost::sp_scalar_constructor_hook( px );
#endif
}
#ifndef BOOST_NO_AUTO_PTR
explicit scoped_ptr( std::auto_ptr<T> p ) BOOST_NOEXCEPT
: px( p.release() )
{
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
boost::sp_scalar_constructor_hook( px );
#endif
}
#endif
//析构函数
~scoped_ptr()
{
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
boost::sp_scalar_destructor_hook( px );
#endif
boost::checked_delete( px );
}
void reset(T * p = 0)
{
BOOST_ASSERT( p == 0 || p != px );
this_type(p).swap(*this);
}
T & operator*() const
{
BOOST_ASSERT( px != 0 );
return *px;
}
T * operator->() const
{
BOOST_ASSERT( px != 0 );
return px;
}
//获得原生指针
T * get() const BOOST_NOEXCEPT
{
return px;
}
#include <boost/smart_ptr/detail/operator_bool.hpp>
void swap(scoped_ptr & b) BOOST_NOEXCEPT
{
T * tmp = b.px;
b.px = px;
px = tmp;
}
2.使用:
当使用boost库的时候,必须先要从boost官方网站下载boost的源码,然后将从boost库下载的源代码所在目录包含至自己的项目,并且使用时要指定命名空间为boost。
#include<iostream>
#include<string>
#include<boost/scoped_ptr.hpp>
#include<boost/scoped_array.hpp>
int main()
{
boost::scoped_ptr<int> sp1(new int(10));
//boost::scoped_ptr<int> sp2(sp1); //不能拷贝
boost::scoped_array<std::string> sp2(new std::string[10]);
return 0;
}
5 boost库shared_ptr的使用
void Test_boost_shared_ptr()
{
boost::shared_ptr<int> sp1(new int(10));
std::cout << *sp1 << std::endl; //输出10
boost::shared_ptr<int> sp2(sp1);
std::cout << *sp2 << std::endl; //输出10
boost::shared_array<std::string> sp3(new std::string[10]);
sp3[5] = "111"; //shared_array里面重载了[],所以可以采用下标的方式进行读写
boost::shared_array<std::string> sp4(sp3);
std::cout << sp4[5] << std::endl; //输出111
}
6 C++11里shared_ptr的使用
1.基本使用与boost里shared_ptr的使用方式一致
void Test_Shared_ptr()
{
std::shared_ptr<int> sp1(new int(20));
std::shared_ptr<int> sp2(sp1);
std::cout << *sp2 << std::endl;
}
2.定制删除器:
(1)首先自己定制删除器(例如我定制了一个delete和一个delete[ ])
自己需要编写相应的仿函数,在构造shared_ptr对象时需要传相应仿函数的对象。
template<class T>
struct Delete
{
void operator()(T* ptr)
{
delete ptr;
}
};
template<class T>
struct DeleteArray
{
void operator()(T* ptr)
{
delete[] ptr;
}
};
(2)使用:
void Test_Shared_ptr()
{
DeleteArray<std::string> da;
std::shared_ptr<std::string> sp3(new std::string[10],da);
}