C++智能指针

在C++中,由于频繁的指针操作,程序员常常需要自己申请和释放内存。而对于内存的管理比较复杂,常常因为忘记释放内存而造成内存泄漏以及二次释放等等情况出现程序错误。故而C++11中引入智能指针的概念,方便其对堆内存的管理。

C++98中,曾有auto_ptr的智能指针。auto_ptr所做的事情,就是动态分配对象以及当对象不再需要时自动清理。使用时需要包含头文件#include

 

其部分源代码如下:

C++智能指针_第1张图片

从中我们可以看出auto_ptr的本质也就是一个类模板。

 

这里假如我们在一个函数中有:

double *p=new double;

如果最后我们没有delete p(释放p所指向的内存);那么每调用一次这个函数就会新建一个double大小的空间。(还有其他各种情况导致没有执行到delete语句就完结或者跳转,同样会造成内存泄漏)

而auto_ptr就是为了解决这种问题出现的:

std::auto_ptr pd(new double);

最后其生命周期结束之后会自动回收所指向的内存。

 

相关函数

get()         返回所存储的指针的值(由于pd并非指针而是类,所以要得到指针的值需要get函数,但是由于操作符重载了*pd即为指向空间中存放的数据)

reset()        重置指针,此时pd内部指针所指向的内存被销毁,同时使其值为nullptr。

release()      释放所有权,此时pd内部指针指向nullptr,但之前所指向的内存不会被销毁。

 

*但是auto_ptr只是一种基于破坏性复制的智能指针,没有解决深度复制、写时复制、应用计数和应用链接的问题。

*不要让auto_ptr指向另一个auto_ptr(或其它对象)所拥有的对象,尽管语法上没问题,很多情况下可能也不会发生错误。但是,当一个指针删除该对象后,而其他指针仍然指向一个已被删除的对象,此时对其进行读写操作就会出错。(这个问题就如同普通指针一样)

 

 

在C++11中有shared_ptr, weak_ptr, unique_ptr三种智能指针。(自然同样也是类模板)

首先说unique_ptr

unique_ptr 独占所指向的对象,同一时刻只能有一个 unique_ptr 指向给定对象。并且禁止拷贝语义,只有移动语义实现改变内部指针指向。

中get、reset、release函数仍然同上,但是其不能通过等号赋值给相同类型的对象了,要用move函数pd2=move(pd),这时pd对指向空间的控制权全部交给pd2了,自己指向nullptr。

 

然后说shared_ptr

shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的),在使用引用计数的机制上提供了可以共享所有权的智能指针。shared_ptr对象除了包括一个所拥有对象的指针外, 还必须包括一个引用计数代理对象的指针。

中get函数和release函数仍然同上,reset函数稍有不同(数个shared_ptr对象的内部指针指向一个内存,若一个对象调用reset会将其引用计数减一,然后该对象指向空,此时那个内存并不会被销毁。只有当引用计数减到0的时候才会删除所指向的内存。通俗地说,当还有额外shared_ptr类的指针指向一个内存的时候,reset并不会销毁该内存,但如果只有这一个shared_ptr类的指针指向这里,那么reset之后该内存也会被销毁)。

 

 

最后说weak_ptr

weak_ptr的出现完全是为了弥补shared_ptr的缺陷,其实相比于上一代的智能指针auto_ptr来说, shared_ptr可以说近乎完美,但是通过引用计数实现的它,虽然解决了指针独占的问题,但也引来了引用成环的问题。

对于循环引用的问题我们看下面代码:

#include
#include
using namespace std;

class B;

class A
{
public:
  shared_ptr m_b;
};

class B
{
public:
  shared_ptr m_a;
};
 
int main()
{
  while (true)
  {
    shared_ptr a(new A); //new出来的A的引用计数此时为1
    shared_ptr b(new B); //new出来的B的引用计数此时为1
    a->m_b = b; //B的引用计数增加为2
    b->m_a = a; //A的引用计数增加为2
  }
 
  //b先出作用域,B的引用计数减少为1,不为0,所以堆上的B空间没有被释放,且B持有的A也没有机会被析构,A的引用计数也完全没减少
 
  //a后出作用域,同理A的引用计数减少为1,不为0,所以堆上A的空间也没有被释放
 return 0;
}

weak_ptr本身也是一个类模板,但是不能直接用它来定义一个智能指针的对象,只能配合shared_ptr来使用,可以将shared_ptr的对象赋值给weak_ptr,并且这样并不会改变引用计数的值。weak_ptr的函数有lock、swap、reset、expired、operator=、use_count等,与shared_ptr相比多了lock、expired函数,但是却少了get、operator*、operator->等函数。

weak_ptr指向shared_ptr指针指向的对象的内存,却并不拥有该内存。但是,使用weak_ptr成员lock,则可返回其指向内存的一个shared_ptr对象,且在所指对象内存已经无效时,返回nullptr。由于weak_ptr是指向shared_ptr所指向的内存的,所以,weak_ptr并不能独立存在。

 

 

最后,通过weak_ptr可以解决环形引用的问题,代码如下:

#include 
#include 
using namespace std;

class father;
class son;

class father {
public:
    father() {
        cout << "father !" << endl;
    }
    ~father() {
        cout << "~~~~~father !" << endl;
    }
    void setSon(shared_ptr s) {
        son = s;
    }
private:
    //shared_ptr son;
    weak_ptr son; // 用weak_ptr来替换
};


class son {
public:
    son() {
        cout << "son !" << endl;
    }
    ~son() {
        cout << "~~~~~~son !" << endl;
    }
    void setFather(shared_ptr f) {
        father = f;
    }
private:
    shared_ptr father;
};

void test() {
    shared_ptr f(new father());
    shared_ptr s(new son());
    f->setSon(s);
    s->setFather(f);
}

int main()
{
    test();
    return 0;
}

转载请标明出处,原文地址:https://blog.csdn.net/come_from_pluto

你可能感兴趣的:(C/C++)