智能指针是利用RAII(在对象的构造函数中执行资源的获取(指针的初始化),在析构函数中释放(delete 指针):这种技法把它称之为RAII(Resource Acquisition Is Initialization:资源获取即初始化))来管理资源。
引用计数技术及智能指针的简单实现
基础对象类
class Point
{
public:
Point(int xVal = 0, int yVal = 0) : x(xVal), y(yVal) { }
int getX() const
{
return x;
}
int getY() const
{
return y;
}
void setX(int xVal)
{
x = xVal;
}
void setY(int yVal)
{
y = yVal;
}
private:
int x, y;
};
辅助类
//模板类作为友元时要先有声明
template <typename T>
class SmartPtr;
template <typename T>
class U_Ptr //辅助类
{
private:
//该类成员访问权限全部为private,因为不想让用户直接使用该类
friend class SmartPtr<T>; //定义智能指针类为友元,因为智能指针类需要直接操纵辅助类
//构造函数的参数为基础对象的指针
U_Ptr(T *ptr) : p(ptr), count(1) { }
//析构函数
~U_Ptr()
{
delete p;
}
//引用计数
int count;
//基础对象指针
T *p;
};
智能指针类
template <typename T>
class SmartPtr //智能指针类
{
public:
SmartPtr(T *ptr) : rp(new U_Ptr<T>(ptr)) { } //构造函数
SmartPtr(const SmartPtr<T> &sp) : rp(sp.rp)
{
++rp->count; //复制构造函数
}
SmartPtr &operator=(const SmartPtr<T> &rhs) //重载赋值操作符
{
++rhs.rp->count; //首先将右操作数引用计数加1,
if (--rp->count == 0) //然后将引用计数减1,可以应对自赋值
delete rp;
rp = rhs.rp;
return *this;
}
T &operator *() //重载*操作符
{
return *(rp->p);
}
T *operator ->() //重载->操作符
{
return rp->p;
}
~SmartPtr() //析构函数
{
if (--rp->count == 0) //当引用计数减为0时,删除辅助类对象指针,从而删除基础对象
delete rp;
else
cout << "还有" << rp->count << "个指针指向基础对象" << endl;
}
private:
U_Ptr<T> *rp; //辅助类对象指针
};
使用测试
int main()
{
int *i = new int(2);
{
SmartPtr<int> ptr1(i);
{
SmartPtr<int> ptr2(ptr1);
{
SmartPtr<int> ptr3 = ptr2;
cout << *ptr1 << endl;
*ptr1 = 20;
cout << *ptr2 << endl;
}
}
}
system("pause");
return 0;
}
C++11智能指针
c++ 智能指针主要包括:unique_ptr
,shared_ptr
, weak_ptr
, 这三种,其中auto_ptr
(C++17) 已被遗弃。
unique_ptr
拥有独有对象所有权语义的智能指针 (类模板)
std::unique_ptr
是通过指针占有并管理另一对象,并在 unique_ptr 离开作用域时释放该对象的智能指针。
shared_ptr
拥有共享对象所有权语义的智能指针 (类模板)
std::shared_ptr
是通过指针保持对象共享所有权的智能指针。多个 shared_ptr
对象可占有同一对象。
weak_ptr
由std::shared_ptr
所管理的对象的弱引用 (类模板)
std::weak_ptr
是一种智能指针,它对被 std::shared_ptr
管理的对象存在非拥有性(“弱”)引用。在访问所引用的对象前必须先转换为 std::shared_ptr
。
std::weak_ptr
用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr
来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr
,此时如果原来的 std::shared_ptr
被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr
同样被销毁为止。
此外,std::weak_ptr
还可以用来避免 std::shared_ptr
的循环引用。
weak_ptr 只能由shared_ptr或者其它的weak_ptr构造
Boost库智能指针(shared_ptr && scoped_ptr && weak_ptr
)
scoped_ptr
Scoped_ptr
不能够被拷贝和赋值,但是可以交换。不能够被拷贝和赋值的原因是赋值运算符和拷贝构造函数都是private。
reset()也可以释放堆对象,就是利用交换实现的。
boost::scoped_ptr
有着更严格的使用限制——不能拷贝。这就意味着:boost::scoped_ptr
指针是不能转换其所有权的。
它能够保证在离开作用域后对象被自动释放。
shared_ptr
shared_ptr
是可以拷贝和赋值的,拷贝行为也是等价的,并且可以被比较,这意味这它可被放入标准库的一般容器(vector,list)和关联容器中(map).
boost::shared_ptr
的管理机制其实并不复杂,就是对所管理的对象进行了引用计数,当新增一个boost::shared_ptr
对该对象进行管理时,就将该对象的引用计数加一;减少一个boost::shared_ptr
对该对象进行管理时,就将该对象的引用计数减一,如果该对象的引用计数为0的时候,说明没有任何指针对其管理,才调用delete释放其所占的内存。
shared_ptr
并不能对循环引用的对象内存自动管理(这点是其它各种引用计数管理内存方式的通病)。
不要构造一个临时的shared_ptr
作为函数的参数。
构造一个临时的shared_ptr
作为函数的参数:
这个问题与异常和函数参数的求值顺序有关。C++构造函数发生的异常将不会调用析构函数。例如f(shared_ptr<int>(new int(2)), g())
。构造了临时的 shared_ptr
,这就为内存泄漏留下了可乘之机。因为函数参数的求值顺序是不确定的,new int(2)
首先被求值,g()
第二个是有可能的,如果 g 抛出一个异常,我们永远也不可能到达 shared_ptr
的构造函数。new int(2)
申请的资源没有机会被释放。
weak_ptr
弱引用并不修改该对象的引用计数,这意味这弱引用它并不对对象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存.
boost::weak_ptr
必须从一个boost::share_ptr
或另一个boost::weak_ptr
转换而来,这也说明,进行该对象的内存管理的是那个强引用的boost::share_ptr
。boost::weak_ptr
只是提供了对管理对象的一个访问手段。
boost::weak_ptr
除了对所管理对象的基本访问功能(通过get()
函数)外,还有两个常用的功能函数:expired()
用于检测所管理的对象是否已经释放;lock()
用于获取所管理的对象的强引用指针。
weak_ptr<T>
的引入主要是为了解决shared_ptr
的循环引用的问题.
CComPtr类
用于管理COM接口指针的智能指针类。
ATL使用CComPtr和CComQIPtr管理COM接口指针。两者都源自CComPtrBase,并且都执行自动引用计数
CComPtr被称为智能指针,是ATL提供的一个模版类,能够从语法上自动完成AddRef和Release。(源代码在atlbase.h中)
参考:
- C++ 引用计数技术及智能指针的简单实现(文章写的太好了,转过了以后用)
- 弱引用
weak_ptr
解决shared_ptr
的循环引用 - 将智能指针作为函数输入参数的两个问题