使用c++,除了c++的文法外,指針是我們面臨的最的大一個問題,由于使用不當就會導緻程式意外退出,或着記憶體的占用越來越多,總結起來這些錯誤由以下三個原因造成。
1 野指針:指針指向的記憶體已經被釋放,但是我們還在使用該指針,或者還在使用之前指向的指針,此時程式會崩潰,也有可能導緻已經釋放的記憶體被重新配置設定給程式使用,造成意想不到的後果。
2 重複釋放:程式嘗試釋放已經被釋放的記憶體單元,或者釋放已經被重新配置設定過的記憶體單元,會導緻重複釋放錯誤。
3 記憶體洩漏:不需要的記憶體單元沒有釋放,一旦程式一直在重複這樣的操作,會導緻程式的記憶體占用月來越高。
雖然顯示的手動管理記憶體,給程式的記憶體管理帶來了很大的自由度,可以高效的利用系統的記憶體,但是也是非常容易出錯的,随着多線程程式的出現和廣泛使用,這樣的問題會更加嚴重,是以c++11通過智能指針來擺脫顯示的記憶體管理,标準庫還實作了‘最小垃圾回收’的支援。
如果你想學習c/c++可以來這個群,首先是三三零,中間是八五九,最後是七六六,裡面有大量的學習資料可以下載下傳。
c++11中通過unique_ptr,shared_ptr和weak_ptr等智能指針來實作自動釋放堆記憶體。
#include <memory>
#include <iostream>
using namespace std;
struct test{
int a;
};
class demo {
public:
demo() {
b = new test();
std::cout << "demo ()" << std::endl;
}
~demo() {
std::cout << "~demo ()" << std::endl;
void say() {
std::cout << "dem::say()==>" << b->a << std::endl;
test *b;
int main()
{
std::cout << "////////////////unique_ptr///////////////////////" << std::endl;
////////////////unique_ptr///////////////////////
unique_ptr<demo> unique_ptr_demo1(new demo);
unique_ptr_demo1->say();
// unique_ptr<demo> unique_ptr_demo2 = unique_ptr_demo1; //無法通過編譯
demo demo2 = *unique_ptr_demo1;
unique_ptr<demo> unique_ptr_demo3 = move(unique_ptr_demo1);
unique_ptr_demo3->say();
// demo demo5 = *unique_ptr_demo1; //運作時錯誤
//
////////////////shared_ptr///////////////////////
std::cout << "////////////////shared_ptr///////////////////////" << std::endl;
shared_ptr<demo> shared_ptr_demo1(new demo());
shared_ptr_demo1->say();
// shared_ptr_demo1.reset();
shared_ptr<demo> shared_ptr_demo2 = shared_ptr_demo1;
shared_ptr_demo1.reset();
// shared_ptr_demo2.reset(); //運作時錯誤2
shared_ptr_demo2->say();
return 0;
這裡我們可以看到不用我們手動釋放指針指向的記憶體,編譯器會幫我們釋放該記憶體,對于析構函數的三次調用,分别對應unique_ptr_demo3,demo2和shared_ptr_demo1.
我們主要看unique_ptr和shared_ptr的差別,unique_ptr跟所指對象的記憶體緊緊的綁定在一起,不和其他的指針共享其指向的記憶體(例子中:無法通過編譯的注釋),也就是說unique_ptr不能指派給别的unique_ptr,更深的講,實際上unique_ptr的拷貝構造函數被删除,是以我們不能指派。但是我們可以通過move語義來竊取unique_ptr的記憶體,但是要注意注釋:運作時錯誤,我們竊取了記憶體後,該指針就不能在使用了,因為他指向的記憶體已經被偷走了,特别是當它的成員變量中有指針時,會造成程式崩潰。
shared_ptr可以指派,允許多個shared_ptr共享一塊記憶體,它采用了引用計數的記憶體管理方式,是以當它放棄了所有權時并不會釋放記憶體,不回影響其他引用這塊記憶體的指針,隻有當引用計數變為0時才會釋放記憶體。我們可以看到例子中 shared_ptr_demo1.reset();并沒有影響shared_ptr_demo2(注:shared_ptr_demo1.reset的注釋此處會徹底釋放記憶體,shared_ptr_demo2會成為一個空指針)是以當我們通過reset顯示的來銷毀指針時,當引用計數沒有為0時隻是把指針設定成nullptr而已。是以既然編輯器會幫我們管理記憶體,如果不是特殊需求,就不要去顯示的調用reset函數來釋放銷毀指針。
class demo{
std::cout << "demo" << std::endl;
template <class t>
void checkptrisvalid(weak_ptr<t>& temp) {
shared_ptr<t> demo = temp.lock();
if (demo != nullptr) {
std::cout << "ptr is good" << std::endl;
}else{
std::cout << "ptr is null" << std::endl;
weak_ptr<demo> weak_ptr_demo1 = shared_ptr_demo1;
checkptrisvalid(weak_ptr_demo1);
shared_ptr_demo2.reset();
最後說道weak_ptr,它的作用不同于以上兩種,官方說法是它可以指向shared_ptr指向的記憶體,但不擁有該記憶體,當調用lock時可以傳回指向記憶體的shared_ptr,上面的例子中有一個檢查指針是否有效的函數,我們可以看到當我們兩次調用reset釋放指針使得記憶體引用計數變為0,釋放記憶體。這裡是2次,而不是三次就非常明确的證明了,weak_ptr并沒有增加引用計數,也就是說并沒有擁有該記憶體。
說到這裡,我們也應該感覺到我們使用最多的應該是shared_ptr了,隻有在我們需要一個指針獨占一塊記憶體是才會用到unique _ptr,而weak_ptr在必要的時候可以用來監測shared_ptr指向的記憶體是否有效,這是一個非常重要且有意義的用法。