看完這篇,別再說不會智能指針了

C++智能指針

1、智能指針的做用

上一篇介紹了內存池的原理和實現,詳情請見內存池設計與實現ios

內存池能夠幫助咱們有效的對內存進行管理,智能指針能夠很方便的管理指針,避免出現內存泄漏;c++

智能指針的做用面試

智能指針的做用:智能指針能夠幫助咱們避免在申請空間後忘記釋放形成內存泄漏的問題;由於智能指針自己是一個類(後面也會本身實現一下),當出了類的做用域的時候,類會調用析構函數進行釋放資源。因此智能指針的做用原理就是在函數結束時自動釋放內存空間,不須要手動釋放內存空間。安全

2、智能指針的原理

咱們這裏的指針指針主要指的是shared_ptr,這也是一種引用計數型智能指針,引用計數顧名思義就是在內存中記錄有多少個智能指針被引用,新增一個引用計數加一,過時引用計數則減一,當引用計數爲0的時候,bash

智能指針纔會釋放資源;服務器

案例一微信

#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
複製代碼

咱們再增長一個傳參,看一下引用計數:學習

案例二ui

#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
複製代碼

經過上面的兩個例子,咱們驗證了最開始說的:智能指針自己是一個類(後面也會本身實現一下),當出了類的做用域的時候,類會調用析構函數進行釋放資源;

3、智能指針的使用

智能指針的使用比較簡單,在咱們程序中須要包含頭文件:

#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
複製代碼

4、智能指針使用注意項

咱們先來看一段代碼:

案例四

#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
複製代碼

很清晰的發現,在最後互相引用的時候,引用計數器沒有加一,最後出做用域的時候就會調用析構函數,進行內存釋放;

5、智能指針的實現

實現智能指針,不管是在面試仍是深入理解智能指針方面,對咱們幫助都是很是大的,理解了上面的原理,咱們動手實現一個智能指針:

智能指針實現代碼

#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 排版

相關文章
相關標籤/搜索