所以還是使用智慧型指標吧
智慧型指標與常規指標的重要區別在於前者負責自動釋放所指向的物件;
智慧型指標也是一種模板,需要提供可以指向的型別;
為了更容易的管理動態記憶體,新的標準庫提供了兩種智慧型指標型別:
shared_ptr允許多個指標指向同乙個物件;
unique_ptr則「獨佔」所指向的物件。
標準庫還定義了乙個名為weak_ptr的伴隨類,它是一種弱引用,指向shared_ptr所管理的物件。以上三種都定義在標頭檔案memory中。
shared_ptrp; //可以指向string的智慧型指標,預設初始化為空指標
//判斷p是否為空,且p是否指向空字串
呼叫該函式在動態記憶體中分配乙個物件空間並初始化它,返回指向此物件的shared_ptr。
在呼叫make_shared函式時,需要指定想要建立的物件型別:
//p指向乙個值為42的整型
auto p = make_shared(42); //賦值操作,指向物件的指標只有p
auto q(p); //拷貝操作,指向物件的指標加1
當執行賦值或拷貝操作的時候,shared_ptr會記錄下有多少個其他的shared_ptr指向同樣的物件,這裡可以認為每個shared_ptr都有乙個關聯的計數器,通常稱之為「引用計數「,當拷貝乙個shared_ptr時,計數器都會增加。當給shared_ptr賦予新值或者被銷毀的時候,計數器就會遞減,當計數器的值減為0時,他就會自動銷毀自己管理的物件,釋放相關聯的記憶體。
程式使用動態記憶體一般出於以下三種原因:
1.程式不知道自己需要使用多少物件;
2.程式不知道使用物件的準確型別;
3.程式需要在多個物件間共享資料。
可以使用關鍵字new來動態分配和初始化物件,但是new無法為其分配的物件命名,而是返回指向該物件的指標。
預設情況下,動態分配的物件時預設初始化的,這意味著內建型別或組合型別的物件的值將是未定義的,而類型別的物件將呼叫預設建構函式進行初始化。
因為編譯器的遍歷,可以使用auto來讓初始化器來推斷我們想要分配的物件的型別,但是只有當括號中僅有單一初始化器時才能使用:
auto p = new auto(obj); //p指向乙個與obj型別相同的物件,用obj初始化
auto p2 = new auto(a,b,c); //錯誤
用new分配const物件是合法的,其必須進行初始化,返回的指標也是const的。
const int* p = new const int(1024);
為了防止記憶體耗盡,在動態記憶體使用完後,應該將它歸還給系統,通過delete表示式來做到:銷毀給定的指標所指向的物件,釋放對應記憶體。
傳遞給delete的指標必須指向動態分配的記憶體或是乙個空指標,釋放並非new分配的記憶體,或將相同的指標值釋放多次,其行為是未定義的。
通過new動態分配的內存在釋放前都是存在的,所以需要自己手動來釋放,這是與智慧型指標不同之處。
1.忘記delete。這就是常說的記憶體洩露問題,查詢記憶體洩露問題很困難,因為通常應用程式執行很長時間,真正耗盡記憶體後,才會被發現;
2.使用已經釋放掉的記憶體。通過釋放記憶體後將指標置為空,有時可以檢測出這種情況;
3.同一塊記憶體釋放兩次。當有兩個指標指向相同的動態空間時可能會發生這種情況,delete第一次的時候,記憶體空間已經被歸還了,再釋放一次,自由空間就可能遭到破壞。
如果物件自己有析構函式,那它在被釋放的時候可以自動執行析構函式,但是如果它沒有析構函式,就可能造成記憶體洩露。為避免這種情況,我們可以定義乙個函式來代替delete,這個函式能對shared_ptr中儲存的指標進行釋放處理。在定義的時候將這個函式作為第二個引數傳入,當該指標被銷毀的時候,不會執行delete,而是呼叫自己定義的刪除函式。
1.不使用相同的內建指標值初始化(或reset)多個智慧型指標;
2.不delete、get()返回的指標;
3.不使用get()初始化或reset另乙個智慧型指標;
4.如果你使用get()返回的指標,記住當最後乙個對應的智慧型指標銷毀後,指標就變為無效的了;
5.如果你使用的智慧型指標管理的資源不是new分配的記憶體,記住傳遞給他乙個刪除器。
乙個unique_ptr「擁有」它所指向的物件,與shared_ptr不同的是在某個時刻只能有乙個unique_ptr指向乙個指定物件。當unique_ptr被銷毀時,它所指向的物件也被銷毀。下表是unique_ptr獨有的操作:
與shared_ptr不同的是,unique_ptr沒有類似make_shared的函式返回指標。當我們定義乙個unique_ptr時,需要將其繫結到乙個new返回的指標。初始unique_ptr必須使用直接初始化形式。因為unique_ptr擁有它指向的物件,所以它沒有賦值或拷貝操作。
雖然不能拷貝和賦值,但是可以通過呼叫release和reset來將指標的所有權轉交給另乙個指標:
unique_ptrp1(new string("this is a good thing"));
cout<
cout<
p2.reset(p3.release()); //釋放了原來的記憶體再指向另外的
cout<
auto p = p1.release();
delete(p);
不能拷貝unique_ptr規則有乙個例外:可以拷貝或賦值乙個將要被銷毀的unique_ptr,比如:
unique_ptrclone(int p)
weak_ptr是一種不控制所指向物件生存週期的智慧型指標,它指向乙個被shared_ptr所管理的物件,但是將它繫結到shared_ptr時並不會改變shared_ptr的引用計數,一旦最後乙個shared_ptr被銷毀,無論有沒有weak_ptr指向物件,物件都會被釋放。
建立乙個weak_ptr時,要用乙個shared_ptr來初始化它。
auto p = make_shared(42);
weak_ptrwp(p);
物件可能不存在,所以不能使用weak_ptr直接訪問物件,需要先呼叫lock檢查物件是否還存在。
weak_ptr是乙個弱引用指標,所以不會對資源的釋放造成影響,也不會操作資源,通過lock返回的shared_ptr來操作資源,這在某些情況下比如迴圈引用造成的問題是很有幫助的,在看到解答,對理解很有幫助。
C Primer第五版筆記 關聯容器
一 型別 關聯容器支援高效的關鍵字查詢和訪問,標準庫中兩個主要的關聯容器是map和set。map中的元素是鍵值對關鍵字表示索引。set中每個元素只包含乙個關鍵字,set支援高效的關鍵字查詢。關聯容器根據三個特性可以分為8種 1 set還是map 2 關鍵字是否可以重複,允許重複的容器名字中都包含單詞...
C Primer第五版筆記 動態陣列
allocator類 為了讓new分配乙個物件陣列,需要在型別名後跟方括號,括號中是分配物件的數目,該數目必須是整型,但不必是常量 new t 分配的記憶體並不是得到乙個陣列,而是得到乙個陣列元素型別的指標,因此不能對動態陣列呼叫begin和end 與普通陣列不同的是,普通陣列不能定義長度為0的陣列...
C Primer第五版筆記 拷貝控制
拷貝賦值函式 析構函式 三 五法則 default和阻止拷貝 乙個類通過定義五種特殊的成員函式來控制其拷貝 賦值 移動以及銷毀操作 拷貝建構函式 拷貝賦值運算子 移動建構函式 移動賦值運算子和析構函式。這些操作都是拷貝控制操作。當實現自己的類的時候要想好在做這些操作時的規則,因為編譯器通常會自動生成...