| C++中的動態(tài)內存與智能指針在C++中,我們通過new(在動態(tài)內存中為對象分配空間并初始化對象)和delete(銷毀該對象,并釋放內存)直接分配和釋放動態(tài)內存。 如下代碼: int *pi = new int;//pi 指向一個未初始化的int 有些人有這樣的疑問,指針一定要new嗎?其實指針和new沒有什么關系。這里的new在動態(tài)內存里為對象分配了內存空間,并返回了一個指向該對象的指針。new是申請堆空間,不new是在棧上分配內存,指針只要指向有效的內存空間就可以。比如: int i; int *p = &i; p->i;//p可以直接使用了 new直接初始化對 象: int *pi = new int(128);//pi指向值為128的對象
string *ps = new string("christian");//*ps 指向“christian”的字符串new分配const對象必須進行初始化,并且返回的執(zhí)著是一個指向const對象的指針: const int *p = new const int(1024);//分配并初始化一個const int; 當然new申請內存分配的時候也不是都會成功的,一旦一個程序用光了他的所有可用內存(雖然這種情況一般很少發(fā)生),new表達式就會失敗。這時候會拋出bad_alloc異常。所以我們需要通過delete來釋放占用的內存。在這里注意delete并不是要刪除指針,而是釋放指針所指的內存。 int i; int *pi = &i; string str = "dwarves"; double *pd = new double(33); delete str; // 錯誤:str不是一個指針 delete pi; // 錯誤:pi指向一個局部變量 delete pd; // 正確 使用new和delete來管理動態(tài)內存常出的一些錯誤: 1.忘記delete,即導致了“內存泄漏”, 2.野指針。在對象已經被釋放掉之后,指針會置為空。這時候我們再次使用,會產生使用非法內存的指針。 不過如果我們需要保留指針,可以在delete以后將nullptr賦予指針,這樣指針就不指向任何對象了,如下代碼: auto p(new auto 42); auto q = p; delete p; p = nullptr; 
 題外話:在測試這個問題的時候,我輸出了下q的值發(fā)現還是42,并且沒有報錯,后來在delete p之后,我又給*p = 19;這個時候 p ,q的值在輸出的時候都是19,也沒有報錯。這個代碼其實根本就是錯誤的了,因為p,q已經沒有有效的內存空間了。這里是釋放了內存,但指針的值不變,指向的內存不會清0,指向的這片內存區(qū)域是待分配的,如果沒有被其他數據覆蓋的話,你就能幸運得輸出這主要原因是你分配的內存小,沒有繼續(xù)分配,被占用的概率小所致。我用的xcode,換到VS下就正常報錯了,是因為VS為了從編譯器的角度上解決緩沖區(qū)溢出等問題,加上的這種功能,C++標準里面沒有這么要求,所有xcode和gcc是不會檢查的。所以在這里 建議大家寫純C++代碼的時候用vs。 3.重復delete,就會使自由空間遭到破壞如: string *ps1 = new string ("one"),*ps2 = ps1;
delete ps1;
delete ps2;//ps2的內存已經被釋放了雖然顯示的管理內存在性能上有一定的優(yōu)勢,但是隨著多線程程序的出現和廣泛使用,內存管理不佳的的情況變得更嚴重。所以C++標準庫中的智能指針很好的解決了這些問題。 auto_ptr以對象的方式管理堆分配的內存,并在適當的時間(比如析構),釋放內存。我們只需要將new操作返回的指針作為auto_ptr的初始值,而不需要調用delete: auto_ptr (new int); 但是auto_ptr在拷貝時會返回一個左值并且不能調用delete[];所以在C++11中改用shared_ptr(允許多個指針指向一個對象),unique_ptr(“獨占”所指向的對象)還有weak_ptr它是一種不控制所指對象生存期的智能指針,指向shared_ptr所管理的對像,在memory頭文件中。 shared_ptr 如下代碼: shared_ptr<int> pi;//指向int 當然我們也可以shared_ptr和new來結合使用,但是必須使用直接初始化的形式來初始化一個智能指針, shared_ptr<int> p1 = new int(1024);//error:必須使用直接初始化的形式 shared_ptr<int> p2(new int(1024)); 但是最好不要混合使用普通指針和智能指針,最安全的分配和使用動態(tài)內存的方法是調用make_shared的標準庫函數。在使用它的時候,必須指定想要創(chuàng)建的對象類型。 shared_ptr<int >pi = make_shared<int>(1);//指向一個值為1的int的shared_ptr shared_ptr<string>ps = make_shared<string>(10,'a');//ps為指向“aaaaaaaaaa”的string 如果我們不傳遞任何參數,對象會進行值初始化 shared_ptr<int>pi = make_shared<int>();//初始化默認值為0; shared_ptr 實現了引用計數型的智能指針,當進行拷貝的時候,計數器都會遞增。而對一個對象進行賦值時,賦值操作符減少左操作數所指對象的引用計數(減1,如果引用計數為減至0,則刪除對象),并增加右操作數所指對象的引用計數(加1),如下代碼: auto p = make_shared<int>(13);//p 指向的對象只有p一個引用者 auto q(p);//此時對象有兩個引用者 auto r = make_shared<int>(10); r = q; 此時r的引用技術為0,r原指對象被自動釋放。q的引用計數增加。 weak_ptr的使用和析構都不會改變shared_ptr的引用計數。weak_ptr可以使用一個非常重要的成員函數lock()從被觀測的shared_ptr獲得一個可用的shared_ptr對象, 從而操作資源。但當expired()==true的時候,lock()函數將返回一個存儲空指針的shared_ptr.如下: #include <boost/smart_ptr.hpp>
#include <boost/make_shared.hpp>
using namespace boost;
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
 shared_ptr<int> sp(new int(10));
 assert(sp.use_count() == 1);
 weak_ptr<int> wp(sp); //從shared_ptr創(chuàng)建weak_ptr
 assert(wp.use_count() == 1);
 if (!wp.expired())//判斷weak_ptr觀察的對象是否失效
 {
  shared_ptr<int> sp2 = wp.lock();//獲得一個shared_ptr
  *sp2 = 100;
  assert(wp.use_count() == 2);
 }
 assert(wp.use_count() == 1);
 return 0;
}當我們定義一個unique_ptr的時候,需要將其綁定到一個new返回的指針。 只能有一個uniqu_ptr指向對象,也就是說它不能被拷貝,也不支持賦值。但是我們可以通過move來移動 std::unique_ptr<int> p1(new int(5)); std::unique_ptr<int> p2 = p1; // 編譯會出錯 std::unique_ptr<int> p3 = std::move(p1); // 轉移所有權, 現在那塊內存歸p3所有, p1成為無效的指針. p3.reset(); //釋放內存. p1.reset(); //實際上什么都沒做. C++中的動態(tài)內存與智能指針,首發(fā)于博客 - 伯樂在線。 | 
|  |