C++日記——Day29:new、delete探祕,智能指針概述、shared_ptr基礎

一、new / delete探祕

1、new,delete是什麼

sizeof關鍵字,不是一個函數。

new,delete關鍵字,不是函數。

malloc,free主要用於C語言,而new,delete用於C++編程,這兩對都用於動態的在堆中分配和釋放內存。

new,delete會調用類的構造與析構函數,malloc與free比new和delete更底層。new / delete具備對 堆上所分配內存進行初始化 / 釋放 的能力,而這些能力是 malloc/free所不具備的。

 

2、operator new() 和 operator delete()函數:

new幹了兩個事情:a、分配內存(通過operator new()來分配內存) b、調用構造函數來初始化內存

void *mypoint = operator new(100); //這種寫法雖然不報錯,但是很不常見

delete也幹了兩個事:a、調用析構函數  b、釋放內存(調用 operator delete()來釋放內存)

 

3、基本new 符合記錄分配內存大小供delete使用

int *p = new int(100);
delete p; // 刪除的時候,編譯器怎麼知道要回收的是4字節;new內部有記錄機制

 

class A {};

A a;
int len = sizeof(a);  //1字節 , 類對象肯定有地址並且至少佔1個字節的空間



class A {
public:
    A() {}
    ~A() {}
};

A a;
int len = sizeof(a);  //1字節,成員函數屬於類不佔用類對象內存


A *pa = new A[2] (); //這裏分配6個字節
int *pi = new int[2]; //這裏分配8個字節

爲什麼動態給類型A分配內存對象時多出來4個字節,而給 內置類型int動態分配內存對象數組時並沒有多出來4個字節?

多出來的4個字節實際上記錄的是在類數組中存放的元素數量,delete時會根據這個數據去找到要析構的對象,而內置類型不存在析構。

int *pi = new int[3]; //這裏分配了12字節,說明系統並沒有多分配出4字節
delete pi; //沒有用[]似乎也可以直接釋放pi這個int數組
delete[] pi; //這種方式是規範的


A *pa = new A[2];
delete pa;   //異常錯誤,規範寫法
delete[] pa;

 

爲什麼 new/delete、new[] / delete [] 要配對使用?

內置類型比如int類型不需要調用析構函數,所以new []的時候系統沒有多分配出4字節;

對於int類型 new[]  ,delete p或者 delete [] p效果一樣;

如果一個對象使用new[]來分配內存,卻用單獨的delete(而不是delete [])來釋放內存,那麼這個對象需要滿足的條件:這個對象類型要麼是內置類型或者是無自定義的析構函數類類型。

class A {
public:
    A() {}
};

A *pa = new A[2]; //這裏分配了2個字節沒有多分配4個字節

當類沒有自定義析構函數時就不會爲類數組多分配記錄對象數量的存儲空間。

 

爲什麼自己一提供析構函數,不用delete[] 來釋放new[] 就會報異常。

a、第一次會正常調用析構函數而不是2次

b、調用operator delete(pa);釋放內存

new的東西也不要用delete[] 來釋放,否則會引起錯亂;程序出錯,自食惡果。

 

智能指針概述

裸指針:直接用new返回的指針。這種指針強大,靈活,但是開發者需要全程負責維護,一不小心就容易出錯,一旦用錯將造成重大問題。

智能指針:解決裸指針可能代碼的各種問題。

智能指針就理解成“裸指針”進行包裝,給裸指針外邊包了一層;包裝後的優點:

1、智能指針能夠“自動釋放所指向的對象”,大家不用再擔心自己new出來的內存忘記釋放了;

建議優先選擇智能指針。使用智能指針的程序更容易編寫和調試。

C++標準庫有四種智能指針:std::

1、auto_ptr(C++98);  unique_ptr(C++11); shared_ptr(C++11);  weak_ptr(C++11);

幫助我們進行動態分配對象的聲明週期管理。能夠有效防止內存泄漏。

目前auto_ptr已經完全被 unique_ptr取代,C++11中反對使用auto_ptr(棄用);

這三種指針都是類模板。我們可以將new獲得的地址賦給他們;

shared_ptr:共享式指針。多個指針指向同一個對象,最後一個指針被銷燬時,這個對象會被釋放。weak_ptr是輔助shared_ptr工作的。

unique_ptr:獨佔式指針;同一時間只有一個指針能夠指向該對象。當然,該對象的所有權還是可以移交出去。

你忘記delete的時候,智能指針幫你delete,智能指針的本分就是幫你delete。

 

 

shared_ptr基礎

共享所有權,不是被一個shared_ptr擁有,而是被多個shared_ptr之間相互協作。shared有額外開銷。

工作原理:引用計數,每個shared_ptr的拷貝都指向相同的內存。所以只有最後一個指向該內存(對象)的shared_ptr指針不需要再指向該對象時,那麼這個share_ptr纔會析構所指向的對象。

最後一個指向該內存對象的shared_ptr在什麼情況下,會釋放該對象呢?

1、這個shared_ptr被析構的時候;

2、這個shared_ptr指向其他對象時;

類似於垃圾回收機制,我們不用再擔心對象合適被delete

類模板,用到<>,<>裏,就是指針可以指向的類型,後邊再跟智能指針名;

shared_ptr<int> pi(new int(100)); //pi指向一個值爲100的int型數據
shared_ptr<int> pi2 = new int(200); //不可以,智能指針是explicit,不可以進行隱式類型轉換,必須直接初始化形式


shared_ptr<int> make(int value){
    return new int(value);   //不可以,無法把new得到的int *轉換成shared_ptr
    return shared_ptr<int>(new int(value))
}

shared_ptr<int> pi3 = make(100);

裸指針可以初始化shared_ptr,但是不推薦,智能指針不要穿插用,否則會出問題。

 

make_shared函數:標準庫裏函數模板,安全,高效的分配和使用sahred_ptr;

它能夠在動態內存中,分配並初始化一個對象,然後返回指向此對象的shared_ptr;

share_ptr<int> p2 = make_shared<int>(100);
share_ptr<string> p3 = make_shared<string>(5, 'a'); //類似於string mystr(5, 'a')

share_ptr<int> p4 = make_shared<int>();
p4 = make_shared<int>(300); //指向新int,p4首先釋放指向值爲0的內存,然後指向這個新的400的內存

auto p5 = make_shared<string>('I love you')

 

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