您好,登錄后才能下訂單哦!
這篇文章給大家介紹C++中怎么使用智能指針,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
一句話帶過:智能指針就是幫我們C++程序員管理動態分配的內存的,它會幫助我們自動釋放new出來的內存,從而避免內存泄漏!
如下例子就是內存泄露的例子:
#include <iostream> #include <string> #include <memory> using namespace std; // 動態分配內存,沒有釋放就return void memoryLeak1() { string *str = new string("動態分配內存!"); return; } // 動態分配內存,雖然有些釋放內存的代碼,但是被半路截胡return了 int memoryLeak2() { string *str = new string("內存泄露!"); // ...此處省略一萬行代碼 // 發生某些異常,需要結束函數 if (1) { return -1; } delete str; // 雖然寫了釋放內存的代碼,但是遭到函數中段返回,使得指針沒有得到釋放 return 1; } int main(void) { memoryLeak1(); memoryLeak2(); return 0; }
memoryLeak1函數中,new了一個字符串指針,但是沒有delete就已經return結束函數了,導致內存沒有被釋放,內存泄露!
memoryLeak2函數中,new了一個字符串指針,雖然在函數末尾有些釋放內存的代碼delete str,但是在delete之前就已經return了,所以內存也沒有被釋放,內存泄露!
使用指針,我們沒有釋放,就會造成內存泄露。但是我們使用普通對象卻不會!
思考:如果我們分配的動態內存都交由有生命周期的對象來處理,那么在對象過期時,讓它的析構函數刪除指向的內存,這看似是一個 very nice 的方案?
智能指針就是通過這個原理來解決指針自動釋放的問題!
C++98 提供了 auto_ptr 模板的解決方案
C++11 增加unique_ptr、shared_ptr 和weak_ptr
auto_ptr 是c++ 98定義的智能指針模板,其定義了管理指針的對象,可以將new 獲得(直接或間接)的地址賦給這種對象。當對象過期時,其析構函數將使用delete 來釋放內存!
用法:
頭文件: #include < memory >
用 法: auto_ptr<類型> 變量名(new 類型)
例 如:
auto_ptr< string > str(new string(“我要成為大牛~ 變得很牛逼!”));
auto_ptr<vector< int >> av(new vector< int >());
auto_ptr< int > array(new int[10]);
例:
我們先定義一個類,類的構造函數和析構函數都輸出一個字符串用作提示!
定義一個私有成員變量,賦值20.
再定義一個私有成員方法用于返回這個私有成員變量。
class Test { public: Test() { cout << "Test的構造函數..." << endl; } ~Test() { cout << "Test的析構函數..." << endl; } int getDebug() { return this->debug; } private: int debug = 20; };
當我們直接new這個類的對象,卻沒有釋放時。。。
int main(void) { Test *test = new Test; return 0; }
可以看到,只是打印了構造函數這個字符串,而析構函數的字符卻沒有被打印,說明并沒有調用析構函數!這就導致了內存泄露!
解決內存泄露的辦法,要么手動delete,要么使用智能指針!
使用智能指針:
// 定義智能指針 auto_ptr<Test> test(new Test);
智能指針可以像普通指針那樣使用:
cout << "test->debug:" << test->getDebug() << endl; cout << "(*test).debug:" << (*test).getDebug() << endl;
這時再試試:
int main(void) { //Test *test = new Test; auto_ptr<Test> test(new Test); cout << "test->debug:" << test->getDebug() << endl; cout << "(*test).debug:" << (*test).getDebug() << endl; return 0; }
自動調用了析構函數。
為什么智能指針可以像普通指針那樣使用???
因為其里面重載了 * 和 -> 運算符, * 返回普通對象,而 -> 返回指針對象。
具體原因不用深究,只需知道他為什么可以這樣操作就像!
函數中返回的是調用get()方法返回的值,那么這個get()是什么呢?
智能指針的三個常用函數:
get() 獲取智能指針托管的指針地址
// 定義智能指針 auto_ptr<Test> test(new Test); Test *tmp = test.get(); // 獲取指針返回 cout << "tmp->debug:" << tmp->getDebug() << endl;
但我們一般不會這樣使用,因為都可以直接使用智能指針去操作,除非有一些特殊情況。
函數原型:
_NODISCARD _Ty * get() const noexcept { // return wrapped pointer return (_Myptr); }
release() 取消智能指針對動態內存的托管
// 定義智能指針 auto_ptr<Test> test(new Test); Test *tmp2 = test.release(); // 取消智能指針對動態內存的托管 delete tmp2; // 之前分配的內存需要自己手動釋放
也就是智能指針不再對該指針進行管理,改由管理員進行管理!
函數原型:
_Ty * release() noexcept { // return wrapped pointer and give up ownership _Ty * _Tmp = _Myptr; _Myptr = nullptr; return (_Tmp); }
reset() 重置智能指針托管的內存地址,如果地址不一致,原來的會被析構掉
// 定義智能指針 auto_ptr<Test> test(new Test); test.reset(); // 釋放掉智能指針托管的指針內存,并將其置NULL test.reset(new Test()); // 釋放掉智能指針托管的指針內存,并將參數指針取代之
reset函數會將參數的指針(不指定則為NULL),與托管的指針比較,如果地址不一致,那么就會析構掉原來托管的指針,然后使用參數的指針替代之。然后智能指針就會托管參數的那個指針了。
函數原型:
void reset(_Ty * _Ptr = nullptr) { // destroy designated object and store new pointer if (_Ptr != _Myptr) delete _Myptr; _Myptr = _Ptr; }
使用建議:
盡可能不要將auto_ptr 變量定義為全局變量或指針;
// 沒有意義,全局變量也是一樣 auto_ptr<Test> *tp = new auto_ptr<Test>(new Test);
除非自己知道后果,不要把auto_ptr 智能指針賦值給同類型的另外一個 智能指針;
auto_ptr<Test> t1(new Test); auto_ptr<Test> t2(new Test); t1 = t2; // 不要這樣操作...
C++11 后auto_ptr 已經被“拋棄”,已使用unique_ptr替代!C++11后不建議使用auto_ptr。
auto_ptr 被C++11拋棄的主要原因
1). 復制或者賦值都會改變資源的所有權
// auto_ptr 被C++11拋棄的主要原因 auto_ptr<string> p1(new string("I'm Li Ming!")); auto_ptr<string> p2(new string("I'm age 22.")); cout << "p1:" << p1.get() << endl; cout << "p2:" << p2.get() << endl; // p2賦值給p1后,首先p1會先將自己原先托管的指針釋放掉,然后接收托管p2所托管的指針, // 然后p2所托管的指針制NULL,也就是p1托管了p2托管的指針,而p2放棄了托管。 p1 = p2; cout << "p1 = p2 賦值后:" << endl; cout << "p1:" << p1.get() << endl; cout << "p2:" << p2.get() << endl;
2). 在STL容器中使用auto_ptr存在著重大風險,因為容器內的元素必須支持可復制和可賦值
vector<auto_ptr<string>> vec; auto_ptr<string> p3(new string("I'm P3")); auto_ptr<string> p4(new string("I'm P4")); // 必須使用std::move修飾成右值,才可以進行插入容器中 vec.push_back(std::move(p3)); vec.push_back(std::move(p4)); cout << "vec.at(0):" << *vec.at(0) << endl; cout << "vec[1]:" << *vec[1] << endl; // 風險來了: vec[0] = vec[1]; // 如果進行賦值,問題又回到了上面一個問題中。 cout << "vec.at(0):" << *vec.at(0) << endl; cout << "vec[1]:" << *vec[1] << endl;
訪問越界了!
3). 不支持對象數組的內存管理
auto_ptr<int[]> array(new int[5]); // 不能這樣定義
所以,C++11用更嚴謹的unique_ptr 取代了auto_ptr!
測試代碼:
#include <iostream> #include <string> #include <memory> #include <vector> using namespace std; class Test { public: Test() { cout << "Test的構造函數..." << endl; } ~Test() { cout << "Test的析構函數..." << endl; } int getDebug() { return this->debug; } private: int debug = 20; }; // 不要定義為全局變量,沒有意義 //auto_ptr<Test> test(new Test); void memoryLeak1() { //Test *test = new Test; // 定義智能指針 auto_ptr<Test> test(new Test); cout << "test->debug:" << test->getDebug() << endl; cout << "(*test).debug:" << (*test).getDebug() << endl; // get方法 Test *tmp = test.get(); // 獲取指針返回 cout << "tmp->debug:" << tmp->getDebug() << endl; // release方法 Test *tmp2 = test.release(); // 取消智能指針對動態內存的托管 delete tmp2; // 之前分配的內存需要自己手動釋放 // reset方法:重置智能指針托管的內存地址,如果地址不一致,原來的會被析構掉 test.reset(); // 釋放掉智能指針托管的指針內存,并將其置NULL test.reset(new Test()); // 釋放掉智能指針托管的指針內存,并將參數指針取代之 // 忠告:不要將智能指針定義為指針 //auto_ptr<Test> *tp = new auto_ptr<Test>(new Test); // 忠告:不要定義指向智能指針對象的指針變量 //auto_ptr<Test> t1(new Test); //auto_ptr<Test> t2(new Test); //t1 = t2; return; } int memoryLeak2() { //Test *test = new Test(); // 定義智能指針 auto_ptr<Test> test(new Test); // ...此處省略一萬行代碼 // 發生某些異常,需要結束函數 if (1) { return -1; } //delete test; return 1; } int main1(void) { //memoryLeak1(); //memoryLeak2(); //Test *test = new Test; //auto_ptr<Test> test(new Test); //cout << "test->debug:" << test->getDebug() << endl; //cout << "(*test).debug:" << (*test).getDebug() << endl; auto_ptr 被C++11拋棄的主要原因 //auto_ptr<string> p1(new string("I'm Li Ming!")); //auto_ptr<string> p2(new string("I'm age 22.")); // //cout << "p1:" << p1.get() << endl; //cout << "p2:" << p2.get() << endl; //p1 = p2; //cout << "p1 = p2 賦值后:" << endl; //cout << "p1:" << p1.get() << endl; //cout << "p2:" << p2.get() << endl; // 弊端2.在STL容器中使用auto_ptr存在著重大風險,因為容器內的元素必須支持可復制 vector<auto_ptr<string>> vec; auto_ptr<string> p3(new string("I'm P3")); auto_ptr<string> p4(new string("I'm P4")); vec.push_back(std::move(p3)); vec.push_back(std::move(p4)); cout << "vec.at(0):" << *vec.at(0) << endl; cout << "vec[1]:" << *vec[1] << endl; // 風險來了: vec[0] = vec[1]; cout << "vec.at(0):" << *vec.at(0) << endl; cout << "vec[1]:" << *vec[1] << endl; // 弊端3.不支持對象數組的內存管理 //auto_ptr<int[]> array(new int[5]); // 不能這樣定義 return 0; }
auto_ptr是用于C++11之前的智能指針。由于 auto_ptr 基于排他所有權模式:兩個指針不能指向同一個資源,復制或賦值都會改變資源的所有權。auto_ptr 主要有三大問題:
復制和賦值會改變資源的所有權,不符合人的直覺。
在 STL 容器中使用auto_ptr存在重大風險,因為容器內的元素必需支持可復制(copy constructable)和可賦值(assignable)。
不支持對象數組的操作
以上問題已經在上面體現出來了,下面將使用unique_ptr解決這些問題。
所以,C++11用更嚴謹的unique_ptr 取代了auto_ptr!
unique_ptr 和 auto_ptr用法幾乎一樣,除了一些特殊。
unique_ptr特性
基于排他所有權模式:兩個指針不能指向同一個資源
無法進行左值unique_ptr復制構造,也無法進行左值復制賦值操作,但允許臨時右值賦值構造和賦值
保存指向某個對象的指針,當它本身離開作用域時會自動釋放它指向的對象。
在容器中保存指針是安全的
A. 無法進行左值復制賦值操作,但允許臨時右值賦值構造和賦值
unique_ptr<string> p1(new string("I'm Li Ming!")); unique_ptr<string> p2(new string("I'm age 22.")); cout << "p1:" << p1.get() << endl; cout << "p2:" << p2.get() << endl; p1 = p2; // 禁止左值賦值 unique_ptr<string> p3(p2); // 禁止左值賦值構造 unique_ptr<string> p3(std::move(p1)); p1 = std::move(p2); // 使用move把左值轉成右值就可以賦值了,效果和auto_ptr賦值一樣 cout << "p1 = p2 賦值后:" << endl; cout << "p1:" << p1.get() << endl; cout << "p2:" << p2.get() << endl;
運行截圖:
B. 在 STL 容器中使用unique_ptr,不允許直接賦值
vector<unique_ptr<string>> vec; unique_ptr<string> p3(new string("I'm P3")); unique_ptr<string> p4(new string("I'm P4")); vec.push_back(std::move(p3)); vec.push_back(std::move(p4)); cout << "vec.at(0):" << *vec.at(0) << endl; cout << "vec[1]:" << *vec[1] << endl; vec[0] = vec[1]; /* 不允許直接賦值 */ vec[0] = std::move(vec[1]); // 需要使用move修飾,使得程序員知道后果 cout << "vec.at(0):" << *vec.at(0) << endl; cout << "vec[1]:" << *vec[1] << endl;
當然,運行后是直接報錯的,因為vec[1]已經是NULL了,再繼續訪問就越界了。
C. 支持對象數組的內存管理
// 會自動調用delete [] 函數去釋放內存 unique_ptr<int[]> array(new int[5]); // 支持這樣定義
除了上面ABC三項外,unique_ptr的其余用法都與auto_ptr用法一致。
構造
class Test { public: Test() { cout << "Test的構造函數..." << endl; } ~Test() { cout << "Test的析構函數..." << endl; } void doSomething() { cout << "do something......" << endl; } }; // 自定義一個內存釋放其 class DestructTest { public: void operator()(Test *pt) { pt->doSomething(); delete pt; } }; // unique_ptr<T> up; 空的unique_ptr,可以指向類型為T的對象 unique_ptr<Test> t1; // unique_ptr<T> up1(new T()); 定義unique_ptr,同時指向類型為T的對象 unique_ptr<Test> t2(new Test); // unique_ptr<T[]> up; 空的unique_ptr,可以指向類型為T[的數組對象 unique_ptr<int[]> t3; // unique_ptr<T[]> up1(new T[]); 定義unique_ptr,同時指向類型為T的數組對象 unique_ptr<int[]> t4(new int[5]); // unique_ptr<T, D> up(); 空的unique_ptr,接受一個D類型的刪除器D,使用D釋放內存 unique_ptr<Test, DestructTest> t5; // unique_ptr<T, D> up(new T()); 定義unique_ptr,同時指向類型為T的對象,接受一個D類型的刪除器D,使用刪除器D來釋放內存 unique_ptr<Test, DestructTest> t6(new Test);
賦值
unique_ptr<Test> t7(new Test); unique_ptr<Test> t8(new Test); t7 = std::move(t8); // 必須使用移動語義,結果,t7的內存釋放,t8的內存交給t7管理 t7->doSomething();
主動釋放對象
unique_ptr<Test> t9(new Test); t9 = NULL; t9 = nullptr; t9.reset();
放棄對象的控制權
Test *t10 = t9.release();
重置
t9.reset(new Test);
auto_ptr 與 unique_ptr智能指針的內存管理陷阱
auto_ptr<string> p1; string *str = new string("智能指針的內存管理陷阱"); p1.reset(str); // p1托管str指針 { auto_ptr<string> p2; p2.reset(str); // p2接管str指針時,會先取消p1的托管,然后再對str的托管 } // 此時p1已經沒有托管內容指針了,為NULL,在使用它就會內存報錯! cout << "str:" << *p1 << endl;
這是由于auto_ptr 與 unique_ptr的排他性所導致的!
為了解決這樣的問題,我們可以使用shared_ptr指針指針!
熟悉了unique_ptr 后,其實我們發現unique_ptr 這種排他型的內存管理并不能適應所有情況,有很大的局限!如果需要多個指針變量共享怎么辦?
如果有一種方式,可以記錄引用特定內存對象的智能指針數量,當復制或拷貝時,引用計數加1,當智能指針析構時,引用計數減1,如果計數為零,代表已經沒有指針指向這塊內存,那么我們就釋放它!這就是 shared_ptr 采用的策略!
例:
class Person { public: Person(int v) { this->no = v; cout << "構造函數 \t no = " << this->no << endl; } ~Person() { cout << "析構函數 \t no = " << this->no << endl; } private: int no; }; // 仿函數,內存刪除 class DestructPerson { public: void operator() (Person *pt) { cout << "DestructPerson..." << endl; delete pt; } };
引用計數的使用
調用use_count函數可以獲得當前托管指針的引用計數。
shared_ptr<Person> sp1; shared_ptr<Person> sp2(new Person(2)); // 獲取智能指針管控的共享指針的數量 use_count():引用計數 cout << "sp1 use_count() = " << sp1.use_count() << endl; cout << "sp2 use_count() = " << sp2.use_count() << endl << endl; // 共享 sp1 = sp2; cout << "sp1 use_count() = " << sp1.use_count() << endl; cout << "sp2 use_count() = " << sp2.use_count() << endl << endl; shared_ptr<Person> sp3(sp1); cout << "sp1 use_count() = " << sp1.use_count() << endl; cout << "sp2 use_count() = " << sp2.use_count() << endl; cout << "sp2 use_count() = " << sp3.use_count() << endl << endl;
如上代碼,sp1 = sp2; 和 shared_ptr< Person > sp3(sp1);就是在使用引用計數了。
sp1 = sp2; --> sp1和sp2共同托管同一個指針,所以他們的引用計數為2;
shared_ptr< Person > sp3(sp1); --> sp1和sp2和sp3共同托管同一個指針,所以他們的引用計數為3;
構造
1). shared_ptr< T > sp1; 空的shared_ptr,可以指向類型為T的對象
shared_ptr<Person> sp1; Person *person1 = new Person(1); sp1.reset(person1); // 托管person1
2). shared_ptr< T > sp2(new T()); 定義shared_ptr,同時指向類型為T的對象
shared_ptr<Person> sp2(new Person(2)); shared_ptr<Person> sp3(sp1);
3). shared_ptr<T[]> sp4; 空的shared_ptr,可以指向類型為T[]的數組對象 C++17后支持
shared_ptr<Person[]> sp4;
4). shared_ptr<T[]> sp5(new T[] { … }); 指向類型為T的數組對象 C++17后支持
shared_ptr<Person[]> sp5(new Person[5] { 3, 4, 5, 6, 7 });
5). shared_ptr< T > sp6(NULL, D()); //空的shared_ptr,接受一個D類型的刪除器,使用D釋放內存
shared_ptr<Person> sp6(NULL, DestructPerson());
6). shared_ptr< T > sp7(new T(), D()); //定義shared_ptr,指向類型為T的對象,接受一個D類型的刪除器,使用D刪除器來釋放內存
shared_ptr<Person> sp7(new Person(8), DestructPerson());
初始化
1). 方式一:構造函數
shared_ptr<int> up1(new int(10)); // int(10) 的引用計數為1 shared_ptr<int> up2(up1); // 使用智能指針up1構造up2, 此時int(10) 引用計數為2
2). 方式二:使用make_shared 初始化對象,分配內存效率更高(推薦使用)
make_shared函數的主要功能是在動態內存中分配一個對象并初始化它,返回指向此對象的shared_ptr; 用法:
make_shared<類型>(構造類型對象需要的參數列表);
shared_ptr<int> up3 = make_shared<int>(2); // 多個參數以逗號','隔開,最多接受十個 shared_ptr<string> up4 = make_shared<string>("字符串"); shared_ptr<Person> up5 = make_shared<Person>(9);
賦值
shared_ptrr<int> up1(new int(10)); // int(10) 的引用計數為1 shared_ptr<int> up2(new int(11)); // int(11) 的引用計數為1 up1 = up2; // int(10) 的引用計數減1,計數歸零內存釋放,up2共享int(11)給up1, int(11)的引用計數為2
主動釋放對象
shared_ptrr<int> up1(new int(10)); up1 = nullptr ; // int(10) 的引用計數減1,計數歸零內存釋放 // 或 up1 = NULL; // 作用同上
重置
p.reset() ; 將p重置為空指針,所管理對象引用計數 減1
p.reset(p1); 將p重置為p1(的值),p 管控的對象計數減1,p接管對p1指針的管控
p.reset(p1,d); 將p重置為p1(的值),p 管控的對象計數減1并使用d作為刪除器
p1是一個指針!
交換
p1 和 p2 是智能指針
std::swap(p1,p2); // 交換p1 和p2 管理的對象,原對象的引用計數不變 p1.swap(p2); // 交換p1 和p2 管理的對象,原對象的引用計數不變
shared_ptr使用陷阱
shared_ptr作為被管控的對象的成員時,小心因循環引用造成無法釋放資源!
如下代碼:
Boy類中有Girl的智能指針;
Girl類中有Boy的智能指針;
當他們交叉互相持有對方的管理對象時…
#include <iostream> #include <string> #include <memory> using namespace std; class Girl; class Boy { public: Boy() { cout << "Boy 構造函數" << endl; } ~Boy() { cout << "~Boy 析構函數" << endl; } void setGirlFriend(shared_ptr<Girl> _girlFriend) { this->girlFriend = _girlFriend; } private: shared_ptr<Girl> girlFriend; }; class Girl { public: Girl() { cout << "Girl 構造函數" << endl; } ~Girl() { cout << "~Girl 析構函數" << endl; } void setBoyFriend(shared_ptr<Boy> _boyFriend) { this->boyFriend = _boyFriend; } private: shared_ptr<Boy> boyFriend; }; void useTrap() { shared_ptr<Boy> spBoy(new Boy()); shared_ptr<Girl> spGirl(new Girl()); // 陷阱用法 spBoy->setGirlFriend(spGirl); spGirl->setBoyFriend(spBoy); // 此時boy和girl的引用計數都是2 } int main(void) { useTrap(); system("pause"); return 0; }
運行截圖:
可以看出,程序結束了,但是并沒有釋放內存,這是為什么呢???
如下圖:
當我們執行useTrap函數時,注意,是沒有結束此函數,boy和girl指針其實是被兩個智能指針托管的,所以他們的引用計數是2
useTrap函數結束后,函數中定義的智能指針被清掉,boy和girl指針的引用計數減1,還剩下1,對象中的智能指針還是托管他們的,所以函數結束后沒有將boy和gilr指針釋放的原因就是于此。
所以在使用shared_ptr智能指針時,要注意避免對象交叉使用智能指針的情況! 否則會導致內存泄露!
當然,這也是有辦法解決的,那就是使用weak_ptr弱指針。
針對上面的情況,還講一下另一種情況。如果是單方獲得管理對方的共享指針,那么這樣著是可以正常釋放掉的!
例如:
void useTrap() { shared_ptr<Boy> spBoy(new Boy()); shared_ptr<Girl> spGirl(new Girl()); // 單方獲得管理 //spBoy->setGirlFriend(spGirl); spGirl->setBoyFriend(spBoy); }
反過來也是一樣的!
這是什么原理呢?
首先釋放spBoy,但是因為girl對象里面的智能指針還托管著boy,boy的引用計數為2,所以釋放spBoy時,引用計數減1,boy的引用計數為1;
在釋放spGirl,girl的引用計數減1,為零,開始釋放girl的內存,因為girl里面還包含有托管boy的智能指針對象,所以也會進行boyFriend的內存釋放,boy的引用計數減1,為零,接著開始釋放boy的內存。最終所有的內存都釋放了。
weak_ptr 設計的目的是為配合 shared_ptr 而引入的一種智能指針來協助 shared_ptr 工作, 它只可以從一個 shared_ptr 或另一個 weak_ptr 對象構造, 它的構造和析構不會引起引用記數的增加或減少。 同時weak_ptr 沒有重載*和->但可以使用 lock 獲得一個可用的 shared_ptr 對象。
弱指針的使用;
weak_ptr wpGirl_1; // 定義空的弱指針
weak_ptr wpGirl_2(spGirl); // 使用共享指針構造
wpGirl_1 = spGirl; // 允許共享指針賦值給弱指針
弱指針也可以獲得引用計數;
wpGirl_1.use_count()
弱指針不支持 * 和 -> 對指針的訪問;
在必要的使用可以轉換成共享指針 lock();
shared_ptr<Girl> sp_girl; sp_girl = wpGirl_1.lock(); // 使用完之后,再將共享指針置NULL即可 sp_girl = NULL;
使用代碼:
shared_ptr<Boy> spBoy(new Boy()); shared_ptr<Girl> spGirl(new Girl()); // 弱指針的使用 weak_ptr<Girl> wpGirl_1; // 定義空的弱指針 weak_ptr<Girl> wpGirl_2(spGirl); // 使用共享指針構造 wpGirl_1 = spGirl; // 允許共享指針賦值給弱指針 cout << "spGirl \t use_count = " << spGirl.use_count() << endl; cout << "wpGirl_1 \t use_count = " << wpGirl_1.use_count() << endl; // 弱指針不支持 * 和 -> 對指針的訪問 /*wpGirl_1->setBoyFriend(spBoy); (*wpGirl_1).setBoyFriend(spBoy);*/ // 在必要的使用可以轉換成共享指針 shared_ptr<Girl> sp_girl; sp_girl = wpGirl_1.lock(); cout << sp_girl.use_count() << endl; // 使用完之后,再將共享指針置NULL即可 sp_girl = NULL;
當然這只是一些使用上的小例子,具體用法如下:
請看Boy類
#include <iostream> #include <string> #include <memory> using namespace std; class Girl; class Boy { public: Boy() { cout << "Boy 構造函數" << endl; } ~Boy() { cout << "~Boy 析構函數" << endl; } void setGirlFriend(shared_ptr<Girl> _girlFriend) { this->girlFriend = _girlFriend; // 在必要的使用可以轉換成共享指針 shared_ptr<Girl> sp_girl; sp_girl = this->girlFriend.lock(); cout << sp_girl.use_count() << endl; // 使用完之后,再將共享指針置NULL即可 sp_girl = NULL; } private: weak_ptr<Girl> girlFriend; }; class Girl { public: Girl() { cout << "Girl 構造函數" << endl; } ~Girl() { cout << "~Girl 析構函數" << endl; } void setBoyFriend(shared_ptr<Boy> _boyFriend) { this->boyFriend = _boyFriend; } private: shared_ptr<Boy> boyFriend; }; void useTrap() { shared_ptr<Boy> spBoy(new Boy()); shared_ptr<Girl> spGirl(new Girl()); spBoy->setGirlFriend(spGirl); spGirl->setBoyFriend(spBoy); } int main(void) { useTrap(); system("pause"); return 0; }
在類中使用弱指針接管共享指針,在需要使用時就轉換成共享指針去使用即可!
自此問題完美解決!
不要把一個原生指針給多個智能指針管理;
int *x = new int(10);
unique_ptr< int > up1(x);
unique_ptr< int > up2(x);
// 警告! 以上代碼使up1 up2指向同一個內存,非常危險
或以下形式:
up1.reset(x);
up2.reset(x);
記得使用u.release()的返回值;
在調用u.release()時是不會釋放u所指的內存的,這時返回值就是對這塊內存的唯一索引,如果沒有使用這個返回值釋放內存或是保存起來,這塊內存就泄漏了.
禁止delete 智能指針get 函數返回的指針;
如果我們主動釋放掉get 函數獲得的指針,那么智能 指針內部的指針就變成野指針了,析構時造成重復釋放,帶來嚴重后果!
禁止用任何類型智能指針get 函數返回的指針去初始化另外一個智能指針!
shared_ptr< int > sp1(new int(10));
// 一個典型的錯誤用法 shared_ptr< int > sp4(sp1.get());
關于C++中怎么使用智能指針就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。