一,为什么要用智能指针
在编写c++程序的时候,让我们最头痛的问题就是内存泄露,也就是说
int* pt = new int;
delete pt;
必须保证new和delete必须成对出现。作为程序猿,可以像使用普通变量一样来使用指针,这个指针可以在恰当的时候被自动释放,智能指针就是这样一个指针,它的任务是保证每一个被动态分配的内存都能够被释放。
看一个例子
如果在DealProcessAdoption有一个异常,会发生什么事情。
所以这段代码很危险,当DealProcessAdoption有一个exception,后面的delete代码就会跳过,从而造成内存泄露。
有两种解决方案
一种是用try catch,另外一种就是用智能指针。二,一个智能指针的实际例子
class intptr
{
private:
int* m_p;
public:
intptr(int* p){ m_p = p; }
~intptr(){ delete m_p; }
int& operator*(){ return *m_p; }
};
我们可以方便的执行以下代码,而不必担心内存泄漏的问题:
somefunction()
{
intptr pi(new int);
*pi = 10;
int a = *pi;
}
以上我们给出的“智能指针”有个致命错误。设想我们执行以下代码会有怎样的情况发生:
void somefunction()
{
intptr pt1(new int);
intptr pt2(new int);
*pt1 = 10;
pt2 = pt1;
}
对于普通指针来说,pt2 = pt1只是让pt2指向与pt1相同的地址,但是对于我们的智能指针来说,pt2原先指向的地址被泄露掉了,而pt1所指向的地址被释放了两次。所以,我们给每个new出来的内存地址对应的分配一个“被指向计数器”,由它记录这块内存地址被多少指针所指向。
三,boost库中的智能指针
但是这个auto_ptr有很多缺点
1、auto_ptr不能共享所有权。
2、auto_ptr不能指向数组
3、auto_ptr不能作为容器的成员。
4、不能通过赋值操作来初始化auto_ptr
std::auto_ptr<int> p(newint(42)); //OK
std::auto_ptr<int> p = newint(42); //ERROR
这是因为auto_ptr 的构造函数被定义为了explicit,不能隐式调用
5、不要把auto_ptr放入容器
boost库的share_ptr
shared_ptr是Boost库所提供的一个智能指针的实现,shared_ptr就是为了解决auto_ptr在对象所有权上的局限性(auto_ptr是独占的),在使用引用计数的机制上提供了可以共享所有权的智能指针.
2. shared_ptr比auto_ptr更安全
3. shared_ptr是可以拷贝和赋值的,拷贝行为也是等价的,并且可以被比较,这意味这它可被放入标准库的一般容器(vector,list)和关联容器中(map)。
四,智能指针的两种实现方式
方案一(引入辅助类)
方案二(句柄类)
测试:
智能指针的另外一种标准实现
namespace smart
{
// 引用计数类.
class smart_count
{
public:
smart_count(int c = 0) : use_count(c) {}
~smart_count() {}
// 增加引用计数, 并返回计数值.
int addref() { return ++use_count; }
// 减少引用计数, 并返回计数值.
int release() { return --use_count; }
private:
// 计数变量.
int use_count;
};
// 智能指针.
template <class T>
class smart_ptr
{
public:
// 构造指针, 并使引用计数置为1.用explicit是防止隐式转换
explicit smart_ptr (T* ptr) : p(ptr), u(new smart_count(1))
{}
// 构造空指针.
explicit smart_ptr () : p(NULL), u(NULL)
{}
// 智能指针析构.
~smart_ptr (void)
{
// 如果引用计数等于0, 则删除数据和引用计数, 并置p为NULL.
// 此处需要注意的是, 共用的u并未置为 NULL, 在其它指针析构
// 时, p为NULL, 则不会重复delete.
if (p && u->release() <= 0)
{
delete p;
delete u;
p = NULL;
}
}
// 智能指针拷贝构造函数.
smart_ptr (const smart_ptr<T>& t)
{
p = t.p;
u = t.u;
if (u) // 必须判断空值.
{
u->addref(); // 增加引用计数.
}
}
// 指针赋值.
void operator= (smart_ptr<T>& t)
{
// 首先将引用计数减1, 然后再判断是否小于0, 如果小于0, 则delete.
if (p && u->release() <= 0)
{
delete p;
delete u;
}
// 直接赋值.
p = t.p;
u = t.u;
if (u) // 必须判断空值.
{
u->addref(); // 增加引用计数.
}
}
// 重载->操作和*操作符.
T *operator-> (void) { return p; }
T& operator *(void) { return *p; }
// 重载!操作符.
bool operator! () const { return !p;}
// 重载指针bool值操作符.
typedef smart_ptr<T> this_type;
typedef T * this_type::*unspecified_bool_type;
operator unspecified_bool_type() const { return !p ? 0: &this_type::p; }
// 得到原指针.
T* get() { return p; }
void reset(T* ptr)
{
// 首先将引用计数减1, 然后再判断是否小于0, 如果小于0, 则delete.
if (p && u->release() <= 0)
{
delete p;
delete u;
}
// 赋值, 如果是NULL, 则不创建引用计数.
p = ptr;
if (p)
u = new smart_count(1);
else
u = NULL;
}
void reset(smart_ptr<T>& t)
{
// 首先将引用计数减1, 然后再判断是否小于0, 如果小于0, 则delete.
if (p && u->release() <= 0)
{
delete p;
delete u;
}
// 赋值.
p = t.p;
u = t.u;
if (u) // 必须判断空值.
{
u->addref(); // 增加引用计数.
}
}
private:
T* p;
smart_count* u;
};
// 重载==操作符.
template<class T, class U> inline bool operator==(smart_ptr<T> & a, smart_ptr<U> & b)
{
return a.get() == b.get();
}
// 重载!=操作符.
template<class T, class U> inline bool operator!=(smart_ptr<T> & a, smart_ptr<U> & b)
{
return a.get() != b.get();
}
}