动态内存与智能指针

来源:互联网 发布:恒生电子 知乎 编辑:程序博客网 时间:2024/05/17 10:05

两种智能指针:

  • shared_ptr : 允许多个指针指向同一对象
  • unique_ptr :“独占”所指向的对象

shared_ptr

shared_ptr类:

  类似vector,智能指针也是模板,所以创建方式为:

shared_ptr<string> p1;//空智能指针shared_ptr,可以指向stringshared_ptr<list<int>> p2;//空智能指针shared_ptr,可以指向int的list

  其他操作方式与普通操作方式都类似:

*p                      //解引用p -> mem                //等价于(*p).memswap(p,q)               //交换p,q中的指针p.swap(q)make_shared<T>(args)    //返回一个shared_ptr,指向一个动态分配的对象(T类型,以args初始化的)shared_ptr<T>p(q)       //拷贝,此操作会增加q的引用计数

make_shared函数:

  返回一个shared_ptr,指向一个动态分配的对象(T类型,以args初始化的)

//指向一个值为42的int的shared_ptrshared_ptr<int> p3 = make_shared<int>(42);//指向一个值为"9999999999"的stringshared_ptr<string> p4 = make_shared<string>(10,'9');//p5指向一个值初始化的int,即,值为0shared_ptr<int> p5 = make_shared<int>();

shared_ptr的拷贝,赋值以及销毁对象:

  当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向的相同的对象

auto p = make_shared<int>(42);//p指向的对象只有p一个引用者auto q(p);//pq指向相同对象,此对象有两个引用者

  一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象:

auto r = make_shared<int >(42);//r指向的int只有一个引用者r = q; //给r赋值,令它指向另一个地址       //递增q"指向的对象"的引用计数       //递减"r原来指向的对象"的引用计数       //r原来指向的对象已没有引用者,会自动释放

当动态对象不再被使用时,shared_ptr类还会自动释放动态对象。

使用new动态分配和初始化对象:

在自由空间分配的内存是无名的:

int *p = new int;//pi指向一个动态分配、未初始化的无名对象//默认情况下,动态分配的对象时默认初始化的string *ps = new string;//空串(类类型默认初始化)int *pi = new int;//pi指向一个未初始化的int(内置类型的值则是未定义的)string *ps1 = new string;//默认初始化为空stringstring *ps = new string();//值初始化为空stringint *pi1 = new int; //默认初始化;*pi1未定义int *pi2 = new int();//值初始化;*pi2为0int *p1 = new int;//如果分配失败,new抛出std::bad_allocint *p2 = new (nothrow) int;//若分配失败,new返回一个空指针

注:对于定义了自己的构造函数的类类型来说,不管采用默认初始化还是值初始化,对象都会通过默认构造函数来初始化。而对于内置类型,值初始化的内置类型对象有着良好的定义的值,而默认初始化的对象的值则是未定义的。

动态内存的管理非常容易出错:

  1. 忘记delete内存。忘记释放动态内存会导致人们常说的“内存泄露”问题

  2. 使用已经释放掉的对象。

  3. 同一块内存释放两次。

相对于查找和修正这些错误来说,制造出这些错误要简单得多。故坚持只使用智能指针,就可以避免所有这些问题。

shared_ptr和new结合使用:

  可以用new返回的指针来初始化智能指针:

shared_ptr<double> p1;//shared_ptr可以指向一个doubleshared_ptr<int> p2(new int(42));//p2指向一个值为42的int

  但是,接受指针参数的智能指针构造函数是explicit的,因此,不能将一个内置指针类型转换为一个智能指针,必须使用直接初始化形式:

shared_ptr<int> p1 = new int(1024);//错误:必须使用直接初始化形式shared_ptr<int> p2(new int(1024));//正确:使用了直接初始化形式

shared_ptr指针的一些函数:

shared_ptr<T> p;p.get()     //返回一个内置类型指针,指向智能指针所管理的对象shared_ptr<T> p(q) //p管理内置指针q所指向的对象p.reset()          //若p是唯一指向其对象的shared_ptr,reset会释放此对象p.reset(q)         //内置指针q,会令p指向qp.reset(q,d)       //同上,但删除p时不会用delete,而是用d

例如:

shared_ptr<int > p(new int(42));int *q = p.get();//正确:但使用q时要注意,不要让它管理的指针被释放p = new int(1024);//错误:不能将一个指针赋予shared_ptrp.reset(new int(1024));//正确:p指向一个新对象

注:永远不要用get初始化另一个智能指针或为另一个智能指针赋值。


unique_ptr

unique_ptr类:

  没有类似make_shared的标准库函数返回一个unique_ptr。当定义一个unique_ptr时,需要将其绑定到一个new返回的指针上。类似shared_ptr,初始化unique_ptr时必须采用直接初始化形式:

unique_ptr<double> p1;          //可以指向一个double的unique_ptrunique_ptr<int> p2(new int(42));//p2指向一个值为42的int//注:因为unique_ptr"拥有"它所指向的对象,因此unique_ptr不支持普通的拷贝或赋值操作unique_ptr<string> p1(new string("sssss"));unique_ptr<string> p2(p1);//错误,unique_ptr不支持拷贝unique_ptr<string> p3;p3 = p1;                  //错误:unique_ptr不支持赋值

unique_ptr的一些操作:

u = nullptr     //释放u指向的对象,将u置为空u.release()     //u放弃对指针的控制权,返回指针,并将u置为空u.reset()       //释放u指向的对象u.reset(q)      //若提供了内置指针q,令u指向这个对象u.reset(nullptr)//否则将u置为空//注:release()只是放弃对指针的控制权,并没有释放对象

  虽然不能拷贝和赋值unique_ptr,但可以调用release或reset将指针的所有权从一个unique_ptr转移给另一个unique:

//将所有权从p1转移给p2unique_ptr<string> p2(p1.release());//release将p1置为空unique_ptr<string> p3(new string("Trex"));//将所有权从p3转移到p2p2.reset(p3.release());//reset释放了p2原来指向的内存

  注意:若我们不用另一个智能指针来保存release返回的指针,我们的程序就要负责资源的释放:

p2.release() ;        //错误:p2不会释放内存,而我们丢失了指针auto p = p2.release();//正确:但我们必须记得delete(p)

注:标准库较早版本包含了一个名为auto_ptr的类,就是《Effective C++》中的auto_ptr,与这里的unique_ptr类似,可以把auto_ptr理解为unique_ptr的一个子集,编写程序时应该使用unique_ptr。

weak_ptr类:

  weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
  当我们创建一个weak_ptr时,要用一个shared_ptr来初始化它:

auto p = make_shared<int>(42);weak_ptr<int> wp(p);//wp弱共享于p;p引用计数未改变

参考:《C++ primer》《Effective C++》