C++11智能指针

        C++的内存管理一直是c++程序员的一个痛点,c++98中提供了auto_ptr智能指针用于管理堆对象,但其支持的操作有限(不支持多个指针指向同一个对象,不支持管理堆数组),限制了其应用。c++11标准中引入了boost库的shared_ptr和unique_ptr智能指针用于内存管理,大大方便了动态对象的管理。本文谈谈shared_ptr和unique_ptr的使用。

shared_ptr

智能指针的构造函数被声明为explicit,所以,我们不能将一个内置指针隐式转换为一个智能指针,其支持的赋值方式如下:

{
        std::shared_ptr p(new Object());       //assignment way 1
        shared_ptr p1 = make_shared(); //assignment way 2
        Object *pObject = new Object();                //assignment way3, equal way 1
        shared_ptr p2(pObject);
} 
  

支持的操作:

p.get()        返回p中保存的指针

p.swap(q)      交换p和q中的指针

p.use_count    返回与p共享对象的智能指针数量

p.unique()     若p.use_count 为1,返回true,否则返回false

p=q       赋值操作,要求p和q都是shared_ptr,且所指向的对象能互相转换。此操作会递减p的引用计数,递增q的引用计数,若p的引用计数变为0,则释放其指向的对象,注意:这里不支持原生指针到智能指针的隐式转换。

另外,和一般指针一样,智能指针支持 *, -> 和 . 运算

    可以使用reset函数来将一个新的指针赋予一个shared_ptr,与赋值类似,reset操作会更新引用计数。此操作经常与unique一起使用,来控制多个shared_ptr共享的对象,例子如下:

if(!p.unique())  //改变底层对象前,检查自己是否是当前对象仅有的用户
    p.reset(new string(*p));  //非唯一用户,在改变对象之前分配新的拷贝
*p += newVal;  //此时已经是唯一用户

shared_ptr用法2:

    shared_ptr的缺省行为是当引用计数为0时删除其所指对象,但可以显示指定一个“删除动作”来改变这一行为,这可以通过创建shared_ptr有一种形式为:shared_ptr p(q, d)  来实现,其中d是一个函数或函数对象(或lamda表达式),可以使在释放对象时所完成的动作由delete变成d所指定的动作,具体的场景如互斥量释放,关闭文件句柄等。如:

 

pthread_mutex_t *pMutex = new pthread_mutex_();
pthread_mutex_init(&pMutex,NULL);
{
pthread_mutex_lock(pMutex);
//do something
...

}

 

使用智能指针形式:

pthread_mutex_t *pMutex = new pthread_mutex_();
pthread_mutex_init(&pMutex,NULL);
{
shared_ptr mutexPtr(pMutex, pthread_mutex_unlock);
pthread_mutex_lock(mutexPtr.get());
//do something
...
}  //will auto unlock

进一步封装成类使用:

class Lock{
public:
explicit Lock(Mutex*pm): mutexPtr(pm, unlock) //使用unlock代替delete
{
    lock(mutextPtr.get());
}
private:
    std::shared_ptr mutexPtr; //使用shared_ptr代替raw pointer
};

tips:

1、 永远不要用get初始化另一个智能指针或者为另一个智能指针赋值,使用get获取内置指针应注意避免空间重复释放问题

如以下用法会导致悬空指针:

shared_ptr p(new int(42));
int *q = p.get();
{
    shared_ptr(q);
}
int foo = *p;  //p指向的内存已经被释放

2、 不要混用原生指针和智能指针

int *x(new int(42));
process(shared_ptr(x));
int j = *x;

其中,process定义如下:

void process(shared_ptr ptr){} //离开作用域后智能指针指向的对象被销毁

unique_ptr

顾名思义,unique_ptr指向一个给定的对象,具有唯一性,即其的引用计数为1,其也是一种智能指针。但与shared_ptr不同,其赋值方式不支持拷贝赋值(即拷贝构造函数的参数为unique_ptr类型),也不支持”=”赋值,其正确的赋值方式如下:

unique_ptrp1(newT(...));  //其中T表示泛型

unique_ptru(new T(...));  //同shared_ptr,用D代替delete操作

    虽然不能通过拷贝或赋值unique_ptr,但可以通过unique_ptr的release或reset操作过度指针所有权(非const)。这两个操作定义如下:

u.release()    u放弃对指针的控制权,返回原生指针,并将u置空

u.reset(q) 如果q不为nullptr,则令u指向这个对象;否则将u置为空

u.reset()      释放u指向的对象      

    应该注意release操作返回了原生指针,但是却不会释放其指向的内存,因此以下语句是错误的:

p2.release();  //p2不会释放内存,造成内存泄漏

unique_ptr的简单使用示例如下:

    unique_ptr p1(new string("p1"));
    cout << "*p1 = " << *p1 << endl;
    unique_ptr p2(p1.release()); //所有权从p1过渡给p2
    cout << "*p2 = " << *p2 << endl;
    unique_ptr p3(new string("p3"));
    p2.reset(p3.release()); //所有权从p3过渡给p2
    cout << "After reset, *p2 = " << *p2 << endl;

输出结果:

*p1= p1

*p2= p1

After reset, *p2 =p3

    不能拷贝unique_ptr的规则有一个例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr,最常见的是从函数返回一个unique_ptr:

    unique_ptr clone(int p)
    {
        return unique_ptrret(new int(p));
    }

p.s:

支持u = nullptr; 将指针置空;

C++98标准库中包含了一个auto_ptr类,其具有unique_ptr的部分特性,可以理解为unique_ptr是auto_ptr的升级版。auto_ptr不支持在容器中保存,也不能从函数中返回auto_ptr。

weak_ptr

weak_ptr指向一个shared_ptr管理的对象,将一个weak_ptr绑定到shared_ptr不会改变shared_ptr的引用计数,也不控制所指对象生存期,即“weak”的特点。其通过下面方式进行初始化:

Weak_ptrw(sp)     与shared_ptrsp指向相同对象的weak_ptr

w = sp               sp可以是shared_ptr或weak_ptr,赋值后w共享p指向的对象

w.reset()            将w置空,但不影响对象的生存期

w.use_count()        即shared_ptr对应的use_count

w.expired()          若w.use_count为0,则为true;否则为false

w.lock()             如果expired为true,返回一个空的shared_ptr;否则返回一个指向w的对象的shared_ptr

其使用如下:

auto p =make_shared(42);
weak_ptrwp(p);  //wp为弱共享,p的use_count不变

由于weak_ptr指向的对象可能不存在,所以使用时必须调用lock检查其指向的对象是否仍存在。

智能指针与动态数组

使用unique_ptr管理动态数组

C++11标准库提供了一个可以管理new分配的数组的unique_ptr版本。使用方式如下:

unique_ptrspArray(new Object[2]);

类型说明符中的方括号指出spArray指向一个Object数组,当spArray销毁它管理的指针时,会自动使用delete[]。

由于spArray指向一个数组,所以我们不能直接使用”.”和”->”成员运算符。这时,我们可以使用下标运算符[]来访问数组中的元素:

u[i]  返回u在数组中位置i处的对象,其中u指向一个数组

使用shared_ptr管理动态数组

与unique_ptr不同,shared_ptr不支持直接管理动态数组,也不支持[]运算符。如果希望使用shared_ptr来管理动态数组,可以通过指定delete []作为删除器(不指定的话默认使用delete作为删除器):

shared_ptrsp(newObject[2], [](int *p){delete[] p;}); 
  

由于不支持下标运算符[],因此,为了访问元素,必须用get获取一个内置指针,然后通过它来访问数组元素,如下所示:

for(size_ti = 0; i < 2; ++i)
{
    *(sp.get() + i) = i;
}

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(智能指针,c,11,shared_ptr,unique_ptr,C/C++)