c++11新特性之智能指針

很多人談到c++,說它特別難,可能有一部分就是因爲c++的內存管理吧,不像java那樣有虛擬機動態的管理內存,在程序運行過程中可能就會出現內存泄漏,然而這種問題其實都可以通過c++11引入的智能指針來解決,相反我還認爲這種內存管理還是c++語言的優勢,因爲盡在掌握。

c++11引入了三種智能指針:

  • std::shared_ptr

  • std::weak_ptr

  • std::unique_ptr

shared_ptr

shared_ptr使用了引用計數,每一個shared_ptr的拷貝都指向相同的內存,每次拷貝都會觸發引用計數+1,每次生命週期結束析構的時候引用計數-1,在最後一個shared_ptr析構的時候,內存纔會釋放。

使用方法如下:

struct ClassWrapper {
    ClassWrapper() {
        cout << "construct" << endl;
        data = new int[10];
    }
    ~ClassWrapper() {
        cout << "deconstruct" << endl;
        if (data != nullptr) {
            delete[] data;
        }
    }
    void Print() {
        cout << "print" << endl;
    }
    int* data;
};


void Func(std::shared_ptr<ClassWrapper> ptr) {
    ptr->Print();
}


int main() {
    auto smart_ptr = std::make_shared<ClassWrapper>();
    auto ptr2 = smart_ptr; // 引用計數+1
    ptr2->Print();
    Func(smart_ptr); // 引用計數+1
    smart_ptr->Print();
    ClassWrapper *p = smart_ptr.get(); // 可以通過get獲取裸指針
    p->Print();
    return 0;
}

智能指針還可以自定義刪除器,在引用計數爲0的時候自動調用刪除器來釋放對象的內存,代碼如下:

std::shared_ptr<int> ptr(new int, [](int *p){ delete p; });

關於shared_ptr有幾點需要注意:

• 不要用一個裸指針初始化多個shared_ptr,會出現double_free導致程序崩潰

• 通過shared_from_this()返回this指針,不要把this指針作爲shared_ptr返回出來,因爲this指針本質就是裸指針,通過this返回可能 會導致重複析構,不能把this指針交給智能指針管理。

class A {
    shared_ptr<A> GetSelf() {
       return shared_from_this();
       // return shared_ptr<A>(this); 錯誤,會導致double free
    }  
};
  • 儘量使用make_shared,少用new。

  • 不要delete get()返回來的裸指針。

  • 不是new出來的空間要自定義刪除器。

  • 要避免循環引用,循環引用導致內存永遠不會被釋放,造成內存泄漏。

using namespace std;
struct A;
struct B;


struct A {
   std::shared_ptr<B> bptr;
   ~A() {
       cout << "A delete" << endl;
  }
};


struct B {
   std::shared_ptr<A> aptr;
   ~B() {
       cout << "B delete" << endl;
   }
};


int main() {
   auto aaptr = std::make_shared<A>();
   auto bbptr = std::make_shared<B>();
   aaptr->bptr = bbptr;
   bbptr->aptr = aaptr;
   return 0;
}

上面代碼,產生了循環引用,導致aptr和bptr的引用計數爲2,離開作用域後aptr和bptr的引用計數-1,但是永遠不會爲0,導致指針永遠不會析構,產生了內存泄漏,如何解決這種問題呢,答案是使用weak_ptr。

weak_ptr

weak_ptr是用來監視shared_ptr的生命週期,它不管理shared_ptr內部的指針,它的拷貝的析構都不會影響引用計數,純粹是作爲一個旁觀者監視shared_ptr中管理的資源是否存在,可以用來返回this指針和解決循環引用問題。

  • 作用1:返回this指針,上面介紹的shared_from_this()其實就是通過weak_ptr返回的this指針,這裏參考我之前寫的源碼分析shared_ptr實現的文章,最後附上鍊接。

  • 作用2:解決循環引用問題。

struct A;
struct B;


struct A {
   std::shared_ptr<B> bptr;
   ~A() {
       cout << "A delete" << endl;
   }
   void Print() {
       cout << "A" << endl;
   }
};


struct B {
   std::weak_ptr<A> aptr; // 這裏改成weak_ptr
   ~B() {
       cout << "B delete" << endl;
   }
   void PrintA() {
       if (!aptr.expired()) { // 監視shared_ptr的生命週期
           auto ptr = aptr.lock();
           ptr->Print();
      }
   }
};


int main() {
   auto aaptr = std::make_shared<A>();
   auto bbptr = std::make_shared<B>();
   aaptr->bptr = bbptr;
   bbptr->aptr = aaptr;
   bbptr->PrintA();
   return 0;
}
輸出:
A
A delete
B delete

unique_ptr

std::unique_ptr是一個獨佔型的智能指針,它不允許其它智能指針共享其內部指針,也不允許unique_ptr的拷貝和賦值。使用方法和shared_ptr類似,區別是不可以拷貝:

using namespace std;


struct A {
   ~A() {
       cout << "A delete" << endl;
   }
   void Print() {
       cout << "A" << endl;
   }
};




int main() {
   auto ptr = std::unique_ptr<A>(new A);
   auto tptr = std::make_unique<A>(); // error, c++11還不行,需要c++14
   std::unique_ptr<A> tem = ptr; // error, unique_ptr不允許移動
   ptr->Print();
   return 0;
}

unique_ptr也可以像shared_ptr一樣自定義刪除器,使用方法和shared_ptr相同。

關於c++11的智能指針的使用就介紹到這裏,大家有問題可以點此留言 ,我會盡快回復~

參考資料

https://www.jianshu.com/p/b6ac02d406a0

https://juejin.im/post/5dcaa857e51d457f7675360b#heading-16

《深入應用c++11:代碼優化與工程級應用》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章