C++中的智能指针笔记

来源:互联网 发布:网络播放平台图标 编辑:程序博客网 时间:2024/05/28 16:03

智能指针是行为类似于指针的对象。因为智能指针是一个对象,那么它也就有析构函数,所以当智能指针对象过期时,它的析构函数会删除指向的内存,而不必我们手动去一个一个的释放内存。

模板auto_ptr是C++98提供的解决方案,C++11已将其摈弃,并提供了其他两种解决方案,unique_ptrhe h和shared_ptr。它的使用方式如下:

以auto_ptr为例,请看下图

</pre><pre name="code" class="cpp"><span style="font-size:14px;">void demo1(){double* pd = new double;*pd = 34.3;return ; }</span>

</pre><span style="font-size:14px;">代码第一条是在为pd和一个double值分配存储空间,pd保存的是存储空间的地址(new double 返回的是一个地址);</span><p></p><p><span style="font-size:14px;">代码第二条是将34.3复制到动态内存中;</span></p><p><span style="font-size:14px;">代码第三条是删除pd,34.3被保留在动态内存中,如果不释放将导致内存泄漏。</span></p><p><pre name="code" class="cpp"><span style="font-size:14px;">void demo2(){auto_ptr<double>  ap(new double);*pd = 34.3;return ; }</span>
代码第一条是在为ap和一个double值分配存储空间,pd保存的是存储空间的地址(new double 返回的是一个地址);
代码第二条是将34.3复制到动态内存中;

代码第三条删除ap,ap的析构函数会释放动态内存。

以上是智能指针的用法,而要创建智能指针对象,必须包含头文件memory。

      智能指针不支持隐式转换,如:

shared_ptr<double> pd;

double*  p_reg = new double;

pd = p_reg; //错误!不允隐式转换

pd = shared_ptr<double>(p_reg);     //正确!显示转换可以

shared_ptr<double>  pshared = p_reg;       ///错误!不允隐式转换

shared_ptr<double> pshared(p_reg);//正确!显示转换可以

有一种情况三种指针都应该避免:

string vaction("I Love You!");

shared_ptr<string> pvac(&vaction);//错误!

pvac过期时,程序将把delete运算符用于非堆内存,这是错误的

下面是有关智能指针的注意事项,先看下面的代码

auto_ptr<string>   ps( new string("I Love You!") );

auto_ptr<string>   vocation;

vocation = ps; //后果有点严重

因为两个指针将指向同一个string对象,这样的后果是程序会删除同一个对象两次——一次是ps过期时,一次是vocation过期时。要避免这种问题:

1.定义赋值运算符,使之执行深度复制。这样两个指针将指向不同的对象,其中的一个对象是另一个对象的副本。

2.建立所有权概念,对于特定的对象,只能有一个智能指针可以拥有它,这样只有拥有对象的智能指针的构造函数会删除对象。然后让赋值操作转让所有权。这就是用于auto_ptr和unique_ptr的策略,但是后者更严格。

3.创造智能更高的指针,跟踪引用特定对象的智能指针数。这称为引用计数。例如,赋值时,计数将加1,而指针过期时,计数减1.仅当最后一个指针过期时,才调用delete。这是shared_ptr采用的策略。

为什么unique_ptr要比auto_ptr更严格?请看下面的代码:

auto_ptr<string> p1( new string("auto") );

auto_ptr<string> p2;

p2 = p1;

在第行代码里,p2接管string对象的所有权后,p1的所有权将被剥夺。前面说过,这是件好事,可防止p1和p2的析构函数试图删除同一个对象;但如果程序随后试图使用p1,灾难就会发生,因为p1不再指向有效的数据。但是编译器没有报错,这就是和unique_ptr的区别所在。如果把上面的三行代码中的auto_ptr换成unique_ptr后,第三行代码编译器会认为非法,避免了p1不再指向有效数据的问题,因此unique_ptr比auto_ptr更安全。

但是有一种独特的方式,将一个智能指针赋给另一个并不会留下危险的悬挂指针。

unique_ptr<string> demo( const char*  str )

{

unique_ptr<string>temp( new string(str) );

return temp;

}

unique_ptr<string> pstr;

pstr = demo("I Love You!");

demo()返回一个临时unique_ptr指针,然后接管了原本返回的unique_ptr所有的对象,而返回的unique_ptr被销毁。这时pstr已经接管了string对象的所有权。这段代码的独特之处在于demo()返回的临时指针很快被销毁,没有机会来使用它来访问无效的数据。换句话来说,没有理由禁止这种赋值,事实上编译器确实允许这种赋值。

总之,程序试图将一个unique_ptr赋给另一个时,如果源unique_ptr是个临时右值,编译器允许这样做,如果源unique_ptr将存在一段时间,编译器将禁止这样做。下面用代码来理解这句话:

unique_ptr<string> p1( new string("Hello") );

unique_ptr<string>  p2;

p2 = p1; //不允许,p1会存在一段时间,知道调用析构函数释放。

unique_ptr<string>  p3;

p3 = unique_ptr<string>( new string("nice") );//允许这样做,因为它调用unique_ptr的构造函数,该构造函数创建的临时对象在其所有 权装                                                                                            转让给p3后就会被销毁。这就是临时右值。

如果你确实想使用p2 = p1这种做法,C++11有一个标准库函数std::move(),让你能够将一个unique_ptr赋给另一个。只需这样修改 p2 = move( p1 );

相比于auto_ptr,unique_ptr还有一个优点,它可用于数组,别忘了必须将delete和new配对,将delete[]和new[]配对。模板auto_ptr使用的而是delete而不是delete[],因此不可用于数组,但是unique_ptr可以。这里有一条智能指针的使用警告,使用new分配内存时才能使用智能指针,不是则不能使用智能指针。

0 0
原创粉丝点击