看完这篇,别再说不会智能指针了

2,943 阅读5分钟

C++智能指针

一、智能指针的作用

上一篇介绍了内存池的原理和实现,详情请见内存池设计与实现

内存池可以帮助我们有效的对内存进行管理,智能指针可以很方便的管理指针,避免出现内存泄漏;

智能指针的作用

智能指针的作用:智能指针可以帮助我们避免在申请空间后忘记释放造成内存泄漏的问题;因为智能指针本身是一个类(后面也会自己实现一下),当出了类的作用域的时候,类会调用析构函数进行释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。

二、智能指针的原理

我们这里的指针指针主要指的是shared_ptr,这也是一种引用计数型智能指针,引用计数顾名思义就是在内存中记录有多少个智能指针被引用,新增一个引用计数加一,过期引用计数则减一,当引用计数为0的时候,

智能指针才会释放资源;

案例一

#include <iostream>
#include <memory>

using namespace std;

class A
{
public:
 A()
 {
  cout << "A Constructor" << endl;
 }
 ~A()
 {
  cout << "A Destruct" << endl;
 }
};



int main()
{
 shared_ptr<A> p = make_shared<A>();
 cout << "count:"<<p.use_count() << endl;
 return 0;
}

结果:

root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# ./test1 
A Constructor
count:1
A Destruct

我们再增加一个传参,看一下引用计数:

案例二

#include <iostream>
#include <memory>

using namespace std;

class A
{
public:
 A()
 {
  cout << "A Constructor" << endl;
 }
 ~A()
 {
  cout << "A Destruct" << endl;
 }
};

void fun(shared_ptr<A>p)
{   
    cout<<"fun count:"<<p.use_count()<<endl;
}

int main()
{
 shared_ptr<A> p = make_shared<A>();
 cout << "count:"<<p.use_count() << endl;
    fun(p);
    cout << "count:"<<p.use_count() << endl;
 return 0;
}

结果:

root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# ./test1 
A Constructor
count:1
fun count:2
count:1
A Destruct

通过上面的两个例子,我们验证了最开始说的:智能指针本身是一个类(后面也会自己实现一下),当出了类的作用域的时候,类会调用析构函数进行释放资源;

三、智能指针的使用

智能指针的使用比较简单,在我们程序中需要包含头文件:

#include <memory>

注意:智能指针是C++11 的标准,在编译的时候需要加上 -std=c++11 的编译参数;

使用智能指针初始化有几种方式,newmake_shared,这里推荐使用make_shared,原因是:make_shared标准库函数,是最安全的分配和使用动态内存的方法,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr;头文件和share_ptr相同。

简单给个案例:

案例三

#include <iostream>
#include <memory>

using namespace std;

class A
{
public:
    A(int count)
    {
        _nCount = count;
    }
    ~A(){}
    void Print()
    {
        cout<<"count:"<<_nCount<<endl;
    }
private:
    int _nCount;
};

int main()
{   
    shared_ptr<A>p = make_shared<A>(10);
    p->Print();
    return 0;
}

编译过程;

root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# g++ -std=c++11 test2.cpp -o test2
root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# ./test2
count:10

四、智能指针使用注意项

我们先来看一段代码:

案例四

#include <iostream>
#include <memory>

using namespace std;

class B;

class A
{
public:
    shared_ptr<B>_pb;
};

class B
{
public:
    shared_ptr<A>_pa;
};

int main()
{
    shared_ptr<A>pa = make_shared<A>();
    shared_ptr<B>pb = make_shared<B>();
    cout<<"pa count:"<<pa.use_count()<<endl;
     cout<<"pb count:"<<pb.use_count()<<endl;
    pa->_pb = pb;
    pb->_pa = pa;
    cout<<"pa count:"<<pa.use_count()<<endl;
    cout<<"pb count:"<<pb.use_count()<<endl;
    return 0;
}

结果;

root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# g++ -std=c++11 test3.cpp -o test3
root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# ./test3
pa count:1
pb count:1
pa count:2
pb count:2

会发现,最终的引用计数为2,那么结束后,引用计数不为0,他们在堆上的空间不会被释放,这就是常说的循环引用

当然,这不是无解的,我们可以另外一种只能指针,只不过这是种弱指针---weak_ptr,这种指针不会增加引用计数,配合shared_ptr,可谓是郎才女貌,皆大欢喜呀!

案例五

#include <iostream>
#include <memory>

using namespace std;

class B;

class A
{
public:
    weak_ptr<B>_pb;
};

class B
{
public:
    weak_ptr<A>_pa;
};

int main()
{
    shared_ptr<A>pa = make_shared<A>();
    shared_ptr<B>pb = make_shared<B>();
    cout<<"pa count:"<<pa.use_count()<<endl;
     cout<<"pb count:"<<pb.use_count()<<endl;
    pa->_pb = pb;
    pb->_pa = pa;
    cout<<"pa count:"<<pa.use_count()<<endl;
    cout<<"pb count:"<<pb.use_count()<<endl;
    return 0;
}

结果:

root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# g++ -std=c++11 test3.cpp -o test3
root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# ./test3
pa count:1
pb count:1
pa count:1
pb count:1

很清晰的发现,在最后互相引用的时候,引用计数器没有加一,最后出作用域的时候就会调用析构函数,进行内存释放;

五、智能指针的实现

实现智能指针,无论是在面试还是深刻理解智能指针方面,对我们帮助都是非常大的,理解了上面的原理,我们动手实现一个智能指针:

智能指针实现代码

#include <iostream>

using namespace std;

template<class T>
class SmartPoint
{
public:
 //构造函数
 SmartPoint(T* p=NULL)
 {
  _ptr = p;
  if (p != NULL)
  {
   _count = 1;
  }
  else
  {
   _count = 0;
  }
 }
 //析构函数
 ~SmartPoint()
 {
  if (--_count == 0)
  {
   delete _ptr;
  }
 }
 //拷贝构造函数
 SmartPoint(const SmartPoint& src)
 {
  if (this != &src)
  {
   _ptr = src._ptr;
   _count = src._count;
   _count++;
  }
 }

 //重载赋值操作符
 SmartPoint& operator=(const SmartPoint& src)
 {
  if (_ptr == src._ptr)
  {
   return *this;
  }
  if (_ptr)
  {
   _count--;
   if (_count == 0)
   {
    delete _ptr;
   }
  }
  _ptr = src._ptr;
  _count = src._count;
  _count++;
  return *this;
 }

 //重载操作符
 T* operator ->()
 {
  if (_ptr)
   return _ptr;
 }

 //重载操作符
 T& operator *()
 {
  if (_ptr)
   return *_ptr;
 }

 size_t use_count()
 {
  return _count;
 }
private:
 T* _ptr;
 size_t _count;

};

void Use(SmartPoint<char> p)
{
 int n = p.use_count();
}

int main()
{
 SmartPoint<char>sp1(new char);
 Use(sp1);
 SmartPoint<char>sp2(sp1);
 SmartPoint<char>sp3;
 sp3 = sp1;
 int n = sp1.use_count();
 return 0;
}

往期精彩文章推荐

想了解学习更多C++后台服务器方面的知识,请关注: 微信公众号:====**CPP后台服务器开发**====

本文使用 mdnice 排版