美文网首页
C++智能指针

C++智能指针

作者: 迢晴 | 来源:发表于2019-02-01 14:58 被阅读0次

1.shared_ptr

shared_ptr类似于vector,也是一个模板类,包含于头文件memory中,基本的定义方式如下

 #include <memory>
 shared_ptr<int> ptr = make_shared<int>();//ptr指向值为0的int
 shared_ptr<int> ptr = make_shared<int>(1);//ptr指向值为1的int
 shared_ptr<vector<int>> ptr = make_shared<vector<int>>(2,1);//ptr指向1个有2个值为1的vector<int>

make_shared是一个函数,通常可以用auto来定义一个变量保存make_shared的结果,这样写起来比较方便

 auto ptr = make_shared<vector<int>>();

每个shared_ptr都有一个关联的计数器,通常称为引用计数

以下情况计数器会增加

  • shared_ptr类型数据初始化另一个shared_ptr类型数据
  • shared_ptr类型数据作为参数传递给函数
  • shared_ptr类型数据作为函数的返回值

以下情况计数器会减少

  • 智能指针被赋予新值
  • 智能指针被销毁时,例如局部的shared_ptr数据离开作用域

举例说明下上述两种情况

 shared_ptr<int> ptr = make_shared<int>(1);
 shared_ptr<int> p = make_shared<int>(2);
 shared_ptr<int> p(ptr);
 
 // 这时p被赋予新值,指向p的引用计数会减少,而指向ptr的引用计数会增加(相当于p被同化了,变成自己人了,所以对方减少,我方增加),如果p指向的引用计数为零时,p指向的内存将会被释放
 shared_ptr<int> fun(T arg)
 {
     ...... //对arg的处理
     return make_shared<int>(arg);
 }
 
 void use_fun(T arg)
 {
     shared_ptr<int> p = fun(arg);//当p离开局部作用域时,它指向的内存将会自动释放掉
 }
 //由于p是唯一引用这块内存的fun函数返回内存的对象。由于p要销毁,所以他指向的对象也会被销毁shared_ptr会通过析构函数来释放指向的内存
 void use_fun(T arg)
 {
     shared_ptr<int> p = fun(arg);
     return p;
 }
 //由于return时返回一个p的拷贝,故当p离开作用域时,它所指向的内存还有其他的使用者,计数器也就不会为零

当计数器为零时,shared_ptr会通过析构函数来释放指向的内存,有效的减少了因为忘记delete而造成的内存泄漏。

shared_ptr还可以和new结合使用

 shared_ptr<int> p1 = new int();//错误
 shared_ptr<int> p1(new int())//正确,使用直接初始化形式

上述错误是因为shared_ptr构造函数时explicit的,不能将一个内置指针隐式转换为一个智能指针,必须使用直接初始化,下述也是同样的原因

 shared_ptr<int> clone(int p)
 {
     return new int(p)//错误
 }
 shared_ptr<int> clone(int p)
 {
     return shared_ptr<int>(new int(p));//显式强制转换成shared_ptr<int>类,正确
 }

智能指针和异常

为使发生异常后资源能被正常释放,智能指针是不二选择

 void f1()
 {
     shared_ptr<int> p(new int(1));
     ....//然后抛出异常
 }//函数结束时会正常释放资源
 
 void f2()
 {
     int *ip = new(int(1));
     ...//此处发生异常 
     delete ip;
 }//在delete之前出现异常,无法执行delete语句,ip所指向的内存不会被释放

常用接口

name function
ptr.use_count() 返回与ptr共享对象的智能指针数量,可能较慢,主要用于调试

注意事项:

  • 但是还是推荐大家使用make_shared来初始化shared_ptr类对象,混用内置指针和智能指针容易出现问题,尤其是使内置指针指向智能指针所负责的对象,因为智能指针会自动释放内存,当智能指针释放这块内存后,再使用内置指针来进行操作是一种未定义的行为。
  • 不要使用get()函数初始化另一个智能指针或为另一个智能指针赋值,两个智能指针指向同一块内存,当一个指针被销毁时,内存释放,另一个指针的操作也是一种未定义行为

2.unique_ptr

unique_ptr与shared_ptr不同,unique_ptr在某一个时刻只能有一个unique_ptr指向给定对象。当unique_ptr被销毁时,它所指向的对象也将被销毁

其基本定义方式如下

 unique_ptr<string> p1(new string("hello world"));//p1指向值为hello world的string

因为unique_ptr不支持拷贝和赋值,所以以下操作错误

 unique_ptr<string> p2(p1);//错误
 unique_ptr<string> p3 = p1;//错误

那该怎么进行unique_ptr类变量的相互传值呢

 unique_ptr<string> p2(p1.release());//将p1的所有权转移给p2,p1置空
 unique_ptr<string> p3(new string("linux"));
 p2.reset(p3.release());//reset释放了p2所指向的内存,把p3的值给p2,使得p2指向p3原本指向的内存,p3因为release置空

虽说unique_ptr不能拷贝和赋值,但是将要销毁的是例外,其实这算一种特殊的"拷贝",属于移动操作

 unique_ptr<int> clone(int p)
 {
     return unique_ptr<int>(new int(p))
 }
name function
u.release() u放弃对指针的控制权,返回指针,并将u置空
u.reset() 释放u所指向的对象
u.reset(q) 如果提过了内置指针q,另u指向这个对象;否则将u置空

unique_ptr与动态数组

定义方式如下

unique_ptr<int[]> ptr1(new int[3]);
unique_ptr<int[]> ptr2(new int[3]{1,2,3});

unique_ptr指向一个数组时,不能使用点和箭头运算符,因为指针指向的是一个数组而不是单个对象,当我们想访问数组时,可以通过下标来访问

for(auto i = 0; i < n; i++)
{
    cout << ptr2[i] << endl;
}

补充下早期的auto_ptr类,它具有unique_ptr类功能的一部分,虽然auto_ptr扔是标准库的一部分,在编写程序时应该用unique_ptr代替

相关文章

网友评论

      本文标题:C++智能指针

      本文链接:https://www.haomeiwen.com/subject/jgldsqtx.html