C++智能指針詳解
優(yōu)缺點:
- 智能指針由原始指針的封裝,優(yōu)點是可以自動分配內存,不用擔心內存泄漏問題。
- 用于解決獨占/共享所有權指針的釋放,傳輸?shù)葐栴}。
- 但是沒有原始指針方便。
一. unique_ptr獨占指針
特點
都是圍繞獨占展開
特點一: 如其名,獨占。也就是說同一個內存空間同時只能有一個指針來管理。
int* pi = new int(10); //利用傳統(tǒng)指針在堆區(qū)開辟一個存放整數(shù)的區(qū)域 std::unique_ptr<int> u_pi_01{pi};//通過傳統(tǒng)指針創(chuàng)建智能指針 std::unique_ptr<int> u_pi_02{pi};//有意讓兩個獨占指針同時指向同一個內存區(qū)域
這么寫編譯器不會報錯,但運行時會提示 error:double free detected in tcache 2
這也就印證了第一個特點,一個內存區(qū)域只能由一個指針管理。
特點二: 當指針超出作用域時,內存自動釋放
//由于指針的本質也是變量,離開作用范圍就會自動釋放 //因此我們需要通過在外部創(chuàng)建變量來保存指針所保存的地址 int* external_pi_1;//用于存儲傳統(tǒng)指針的地址 int* external_pi_2;//用于存儲智能指針的地址 { int *pi = new int(10);//使用傳統(tǒng)指針在堆區(qū)開辟內存存儲整形 external_pi_1 = pi; std::unique_ptr<int> u_pi{new int(10)};//使用智能指針在堆區(qū)開辟內存存儲 external_pi_2 = u_pi.get(); std::cout << *external_pi_1 << std::endl;//輸出10 std::cout << *external_pi_2 << std::endl;//輸出10 } std::cout << *external_pi_1 << std::endl;//輸出10 std::cout << *external_pi_2 << std::endl;//輸出0
可見傳統(tǒng)指針在局部作用域中開辟的內存在外部同樣可以訪問,也就是說我們使用傳統(tǒng)指針開辟內存之后在離開作用域時需要加上釋放內存的操作,不然會造成內存泄漏。
而智能指針我們不需要手動釋放內存,在離開作用域后會自動釋放。
特點三:由于特點一,修改指針不可以copy ,只能Move(轉移歸屬權)
std::unique_ptr<int> u_pi1 = std::make_unique<int>(10); //std::unique_ptr<int> u_pi2 = u_pi1;//嘗試用拷貝的方式共享內存,error:可別忘了這是獨占指針 std::unique_ptr<int> u_pi2 = move(u_pi1);使用move方法轉移內存擁有權。
也就是說,通過move函數(shù),把指針u_pi1所指內存中的值掏空,然后安到指針u_pi2所指的內存上。
創(chuàng)建方式
方式一: 通過已有的傳統(tǒng)指針創(chuàng)建
int* pi = new int(10); //使用傳統(tǒng)指針在堆區(qū)開辟一個空間 std::unique_ptr<int> u_pi{pi};//利用創(chuàng)通指針創(chuàng)建智能指針
方式二: 通過new方法創(chuàng)建
std::unique_ptr<int> u_pi{new int(10)};
方式三: 通過std :: make_unique創(chuàng)建
std::unipue_ptr<int> u_pi = std::make_unique<int>(10);
傳遞方式
方式一: 通過move(),轉移擁有權.
void show(std::unique_ptr<int> u_pi) { std::cout<<*u_pi<<std::endl; } void test() { std::unique_ptr<int> u_pi{new int(10)}; show(move(u_pi)); //通過move轉移擁有權 }
注意:將指針的擁有權轉入函數(shù)中后,在原作用域指針將被釋放,而該指針將在函數(shù)調用結束時釋放。也就是說,將智能指針以move的形式傳入函數(shù)后,在原作用域不能再使用該指針。
方式二: 通過引用傳遞
void show(const std::unique_ptr<int> &u_pi)//加cosnt 不是不能改變指向的值,不能改變指針的指向 { std::cout << *u_pi << std::endl; //u_pi.reset();加了const所以不能清空 } void test() { std::unique_ptr<int> u_pi{new int(10)}; show(u_pi); }
注意: 將指針以引用的方式傳入函數(shù),那么該指針在原作用域依然存活,并可以和所調用函數(shù)共同操作該內存空間數(shù)據(jù)。
方式三: 鏈式傳遞
std::unique_ptr<Person> get_unique(std::string str) { std::unique_ptr<Person> u_pi{new Person(str)}; return u_pi; } void test() { get_unique("hua")->show();//鏈式 }
簡單使用
- 通過get()獲取地址
- 可以通過->調用成員函數(shù)
- 可以通過*調用解引用
- 通過reset()清空指針
class Person { public: Perosn(std::string name):m_name(name){}; void show() { std::cout<<"name is "<<m_name<<std::endl; } private: std::string m_name; }; int main() { std::unique_ptr<Person> u_p{new Person("kimi")}; //用自定義類型創(chuàng)建 u_p->show();//可以通過->調用函數(shù) (*u_p).show();//通過*解引用 std::cout<<u_p.get()<<std::endl;//通過get()獲取地址 u_p.reset();//清空指針 return 0; }
隱藏危險
用已有指針創(chuàng)建時,沒有及時清空傳統(tǒng)指針,導致同時有兩個指針指向這塊已經(jīng)被“獨占”的區(qū)域。
int* pi = new int(10); std::unique_ptr<int> u_pi{pi};//使用傳統(tǒng)指針創(chuàng)建,上式開辟的區(qū)域被獨占 *pi = 20; //沒有及時清空,依然可以通過獨占指針以外的方式修改內存
二. shared_ptr 計數(shù)指針
特點
特點一: 可以通過copy共享內存。
std::shared_ptr<int> u_pi_1{new int(10)}; std::shared_ptr<int> u_pi_2 = u_pi_1;//通過復制拷貝
特點二: 通過use_count();來查看計數(shù) ,copy 計數(shù)加一,銷毀計數(shù)減一。
std::shanred_ptr<int> s_pi{new int(10)}; //s_pi.use_count() == 1 std::shanred_ptr<int> s_pi_copy = s_pi; //s_pi.use_count() == 2 s_pi = nullptr;//清空指針 //s_pi_copy.use_count() == 1
特點三: 無論多少指針,都同用一份數(shù)據(jù),因而同一份數(shù)據(jù)的use_count()一致。
std::shared_ptr<int> s_pi{new int(10)}; //s_pi.use_count() == 1 std::shared_ptr<int> s_pi_2 = s_pi; //s_pi.use_count() == 2 std::shared_ptr<int> s_pi_3 = s_pi_2; //s_pi.use_count() == 3 s_pi_2 = bullptr; //清空2指針 //s_pi.use_count() == 2
傳遞方式
- 本質不變,在函數(shù)調用中,因為本身支持復制操作,所以不用加move可以直接傳遞。
- 并且在傳遞到函數(shù)中,use_count() 會增加,并在函數(shù)銷毀時候還原。
- 在函數(shù)中修改指向的值,在外部的指針指向的值也會改變。
- 使用引用傳遞,則在傳遞到函數(shù)中時,計數(shù)不會增加。
void get_use_1(std::shared_ptr<int> s_pi) { std::cout << s_pi.use_count() << std::endl; } void get_use_2(std::shared_ptr<int>& s_pi) { std::cout << s_pi.use_count() << std::endl; } void test() { std::shared_ptr<int> s_pi{new int(10)}; std::cout << s_pi.use_count() << std::endl; get_use_1(s_pi);//在函數(shù)中計數(shù)會增加,但隨著函數(shù)銷毀,計數(shù)復原 get_use_2(s_pi);//以引用方式傳入,指針還是那個指針,計數(shù)不會增加 }
輸出:1 2 1
隱藏危險
share_ptr帶來的循環(huán)依賴問題
class Person { public: void set_friend(share_ptr<Person> p) _friend = p; private: share_ptr<Person> _frient; }; int main() { share_ptr<Person> p1 = make_shared("P1"); share_ptr<Person> p2 = make_shared("P2"); p1->set_friend(P2); p2->set_friend(P1);//造成循環(huán)依賴,在main中的話,不會執(zhí)行析構 }
解決:將_friend屬性改為weak_ptr 。
三. weak_ptr
weak_ptr 是一個不需要所有權的指針,所以我們可以通過用weak_ptr來聲明屬性,解決循環(huán)依賴
class Person { public: void set_friend(share_ptr<Person> p) _friend = p; private: weak_ptr<Person> _frient;//使用weak_ptr解決循環(huán)依賴 }; int main() { share_ptr<Person> p1 = make_shared("P1"); share_ptr<Person> p2 = make_shared("P2"); p1->set_friend(P2); p2->set_friend(P1); }
可以通過lock()來將weak_pte升級為shared_ptr;
std::weak_ptr<Person> w_pi{new Person("hua")}; std::shared_ptr <Person> s_pi2 = w_pi.lock();
到此這篇關于C++智能指針詳解的文章就介紹到這了,更多相關C++智能指針內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!