C++ 智能指针(二) std::unique_ptr

C++ STL智能指针系列:

前言

上篇文章中我们提到auto_ptr,并讲解了它的不足。为了弥补这些不足,C++11提出了unique_ptr来取代auto_ptr。

std::unique_ptr

unique_ptr,翻译过来就是"唯一的指针",从字面上就很好理解。它是独享被管理对象指针所有权的智能指针,也就是说两个指针不能指向同一个资源,复制或赋值都会改变资源的所有权。下面的第三行就会报错:

unique_ptr<Object> pObj1(new Object);
unique_ptr<Object> pObj2(move(pObj1));
cout<<pObj1->num;

从上面的代码我们可以看到unique_ptr不是进行拷贝构造的,而是进行移动构造的。这也是高明之处,C++为解决STL容器内的元素必需支持可复制和可赋值,没有提供拷贝构造函数,只提供了移动构造函数。这就意味着我们无法进行左值复制构造,也无法进行左值复制赋值操作,但允许临时右值赋值构造和赋值,然后问题就解决了。

此外还有unique支持数组作为元素,其析构时会自动检测出数组,并使用delete []

unique_ptr<int[]> pArrayi(new int[10]);

我们可以来看看析构的情况:

unique_ptr<Object[]> pArrayObj(new Object[10]);

输出:

Create Object
Create Object
Create Object
Create Object
Create Object
Create Object
Create Object
Create Object
Create Object
Create Object
Free Resources
Free Resources
Free Resources
Free Resources
Free Resources
Free Resources
Free Resources
Free Resources
Free Resources
Free Resources

unique_ptr用法

在容器中使用unique_ptr

刚才我们讲了为什么unique_ptr可以在STL容器中使用,现在我们来看看具体用法:

vector<unique_ptr<string>> vec;
unique_ptr<string> ps1(new string("Hello"));
unique_ptr<string> ps2(new string("World"));

vec.push_back(std::move(ps1));
vec.push_back(std::move(ps2));

我们要注意赋值时必须使用std::move方法转化为右值才可赋值:

vec[0] = move(vec[1]);	

常用成员方法

unique_ptr中的get()release()reset()方法与auto_ptr相同,此外,unique_ptr还提供了其他的一些函数。

1. operator bool()方法

operator bool():重载了bool,可以直接判断是否为空:

unique_ptr<Object> pObj(new Object);
pObj.reset();
if(pObj){
    cout<<"is not a null pointer"<<endl;
}else{
    cout<<"is a null pointer"<<endl;
}
2. swap()方法

swap()方法可以交换两个unique_ptr的托管对象:

class Object{
public:
    Object(int num):num(num){}
    ~Object(){}
public:
    int num;
};

int main(){
    unique_ptr<Object> pObj1(new Object(123));
    unique_ptr<Object> pObj2(new Object(456));
    cout<<"pointer 1: "<<pObj1->num<<endl;
    cout<<"pointer 2: "<<pObj2->num<<endl;
    pObj1.swap(pObj2);
    cout<<"pointer 1: "<<pObj1->num<<endl;
    cout<<"pointer 2: "<<pObj2->num<<endl;
}

输出内容:

pointer 1: 123
pointer 2: 456
pointer 1: 456
pointer 2: 123
3. operator*() & operator->() & operator

对于单对象,使用operator*()operator->(),方法与auto_ptr一样。对于数组版本,则使用operator[]()

unique_ptr<int[]> pObj(new int[5]);
for(int i=0;i<5;i++)
    cout<<&pObj[i]<<endl;

输出如下:

0x2041660dc00
0x2041660dc04
0x2041660dc08
0x2041660dc0c
0x2041660dc10
4. get_deleter()方法

可以获取unique_ptr的删除器:

unique_ptr<Object> pObj(new Object());
auto del = pObj.get_deleter();

此外,unique_ptr还支持自定义删除器。

自定义deleter

我们先来看看unique_ptr源代码:

template <typename _Tp, typename _Dp = default_delete<_Tp>>
class unique_ptr
{
	...
};

可以看到我们还可以传入一个deleter。自定义deleter的应用场景是假如一个对象,不是通过delete释放的,那么就需要我们使用自定义的deleter。举个例子,C语言的标准文件操作使用FILE*指针作为句柄,然后使用fclose()放资源。我们想要使用unique_ptr代理,使其在离开作用域的时候自动释放:
自定义的deleter:

class FileDeleter{
public:
    void operator()(FILE* _fp){
        if(_fp != nullptr){
            cout<<"Free resource ..."<<endl;
            fclose(_fp);
        }
    }
};

这里是重载了一下()运算符,使其成为一个伪函数。或者我们可以直接使用函数:

void FileDeleter(FILE* _fp){
    if(_fp != nullptr){
        cout<<"Free resource ..."<<endl;
        fclose(_fp);
    }
}

在某一函数中使用:

void scope(){
    unique_ptr<FILE,FileDeleter> file(fopen("test.txt","w"),FileDeleter());
    fputs("test",file.get());
}

如果是函数,注意传入是这样使用:

    unique_ptr<FILE,void (*)(FILE*)> file(fopen("test.txt","w"),FileDeleter);

然后调用函数:

int main(){
    scope();
    cout<<"Has left the scope"<<endl;
}

我们可以看到在退出作用域时就调用了FileDeleter

Free resource ...
Has left the scope
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++智能指针 智能指针_unique_ptr智能指针详解 智能指针详解 作为智能指针的⼀种,unique_ptr 指针⾃然也具备"在适当时机⾃动释放堆内存空间"的能⼒。和 shared_ptr 指针最⼤的不同之处在 于,unique_ptr 指针指向的堆内存⽆法同其它 unique_ptr 共享,也就是说,每个 unique_ptr 指针都独⾃拥有对其所指堆内存空间的所有 权。 这也就意味着,每个 unique_ptr 指针指向的堆内存空间的引⽤计数,都只能为 1,⼀旦该 unique_ptr 指针放弃对所指堆内存空 间的所有权,则该空间会被⽴即释放回收。 unique_ptr 智能指针是以模板类的形式提供的,unique_ptr<T>(T 为指针所指数据的类型)定义在<memory>头⽂件,并位于 std 命名空间 中。因此,要想使⽤ unique_ptr 类型指针,程序中应⾸先包含如下 2 条语句: 1. #include <memory> 2. using namespace std; 第 2 句并不是必须的,可以不添加,则后续在使⽤ unique_ptr 指针时,必须标注std:: 。 unique_ptr智能指针的创建 智能指针的创建 考虑到不同实际场景的需要,unique_ptr<T> 模板类提供了多个实⽤的构造函数,这⾥给读者列举了⼏种常⽤的构造 unique_ptr 智能指针的⽅式。 1) 通过以下 2 种⽅式,可以创建出空的 unique_ptr 指针: 1. std::unique_ptr<int> p1(); 2. std::unique_ptr<int> p2(nullptr); 2) 创建 unique_ptr 指针的同时,也可以明确其指向。例如: 1. std::unique_ptr<int> p3(new int); 由此就创建出了⼀个 p3 智能指针,其指向的是可容纳 1 个整数的堆存储空间。 和可以⽤ make_shared<T>() 模板函数初始化 shared_ptr 指针不同,C++11 标准中并没有为 unique_ptr 类型指针添 加类似的模板函数。 3) 基于 unique_ptr 类型指针不共享各⾃拥有的堆内存,因此 C++11 标准中的 unique_ptr 模板类没有提供拷贝构造函数,只提供 了移动构造函数。例如: 1. std::unique_ptr<int> p4(new int); 2. std::unique_ptr<int> p5(p4);//错误,堆内存不共享 3. std::unique_ptr<int> p5(std::move(p4));//正确,调⽤移动构造函数 值得⼀提的是,对于调⽤移动构造函数的 p4 和 p5 来说,p5 将获取 p4 所指堆空间的所有权,⽽ p4 将变成空指针(nullptr)。 4) 默认情况下,unique_ptr 指针采⽤ std::default_delete<T> ⽅法释放堆内存。当然,我们也可以⾃定义符合实际场景的释放规 则。值得⼀提的是,和 shared_ptr 指针不同,为 unique_ptr ⾃定义释放规则,只能采⽤函数对象的⽅式。例如: 1. //⾃定义的释放规则 2. struct myDel 3. { 4. void operator()(int *p) { 5. delete p; 6. } 7. }; 8. std::unique_ptr<int, myDel> p6(new int); 9. //std::unique_ptr<int, myDel> p6(new int, myDel()); unique_ptr<T>模板类提供的成员⽅法 模板类提供的成员⽅法 为了⽅便⽤户使⽤ unique_ptr 智能指针unique_ptr<T> 模板类还提供有⼀些实⽤的成员⽅法,它们各⾃的功能如表 1 所⽰。 表 1 unique_ptr指针可调⽤的成员函数 成员函数名 成员函数名 功 功 能 能 operator*() 获取当前 unique_ptr 指针指向的数据。 operator->() 重载 -> 号,当智能指针指向的数据类型为⾃定义的结构体时,通过 -> 运算符可以获取其内部的指定成员。 operator =() 重载了 = 赋值号,从⽽可以将 nullptr 或者⼀个右值 unique_ptr 指针直接赋值给当前同类型的 unique_ptr 指针。 operator []() 重载了 [] 运算符,当 unique_ptr 指针指向⼀个数组时,可以直接通过 [] 获取指定下标位置处的数据。 get() 获取当前 unique_ptr 指针内部包含的普通指针。 get_deleter()

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

orbitgw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值