智能指針
參考:https://blog.csdn.net/flowing_wind/article/details/81301001
我們知道除了靜態內存和棧內存外,每個程序還有一個內存池,這部分內存被稱為自由空間或者堆。程序用堆來存儲動態分配的對象即那些在程序運行時分配的對象,當動態對象不再使用時,我們的代碼必須顯式的銷毀它們。
在C++中,動態內存的管理是用一對運算符完成的:new和delete,new:在動態內存中為對象分配一塊空間並返回一個指向該對象的指針,delete:指向一個動態獨享的指針,銷毀對象,並釋放與之關聯的內存。
動態內存管理經常會出現兩種問題:一種是忘記釋放內存,會造成內存泄漏;一種是尚有指針引用內存的情況下就釋放了它,就會產生引用非法內存的指針。
為了更加容易(更加安全)的使用動態內存,引入了智能指針的概念。智能指針的行為類似常規指針,重要的區別是它負責自動釋放所指向的對象。標准庫提供的兩種智能指針的區別在於管理底層指針的方法不同,
shared_ptr允許多個指針指向同一個對象,unique_ptr則“獨占”所指向的對象。標准庫還定義了一種名為weak_ptr的伴隨類,它是一種弱引用,指向shared_ptr所管理的對象,這三種智能指針都定義在memory頭文件中。
常用智能指針:shared_ptr,unique_ptr和weak_ptr
shared_ptr允許多個指針指向同一個對象,unique_ptr則“獨占”所指向的對象。標准庫還定義了一種名為weak_ptr的伴隨類,它是一種弱引用,指向shared_ptr所管理的對象,這三種智能指針都定義在memory頭文件中。
shared_ptr<int> p3 = make_shared<int>(42); shared_ptr<string> p4 = make_shared<string>(10,'9'); shared_ptr<int> p5 = make_shared<int>(); //可以通過調用release或reset將指針所有權從一個(非const)unique_ptr轉移給另一個unique //將所有權從p1(指向string Stegosaurus)轉移給p2 unique_ptr<string> p2(p1.release());//release將p1置為空 unique_ptr<string> p3(new string("Trex")); //將所有權從p3轉移到p2 p2.reset(p3.release());//reset釋放了p2原來指向的內存
shared_ptr和unique_ptr用法:
shared_ptr<T> sp; //空指針,可以執行類型為T 的對象 unique_ptr<T> up; p //p看做一個條件判斷,若p指向一個對象,則為true *p //獲取它指向的對象 p->mem //等價於(*p).mem p.get() //返回P中保存的指針。 swap(p,q) //交換p和q中的指針 p.swap(q)
share_ptr:
make_share<T> (args) //返回一個shared_ptr,指向一個動態分配的類型為T的對象,使用args初始化。 share_ptr<T> p(q) //p是shared_ptr q的拷貝,此時會增加q中計數器,q中的指針必須轉換為T*; p = q //p和q都是shared_ptr,所保存的指針必須能相互轉換。此操作會遞減p的引用計數, //遞增q的引用計數。若p的引用計數為0,則將其管理的內存釋放。 p.unique() //若p.use_count()為1,則返回 true;否則返回 false p.use_count() //返回與p共享對象的智能指針數量;可能很慢,主要用於調試。
unique_ptr:
//由於一個unique_ptr擁有它指向的對象,因此unique_ptr不支持普通的拷貝或賦值操作。
unique_ptr<T> u1 // 空unique_ptr,可以指向類型為T的對象。u1會使用delete來釋放它的指針; unique_ptr<T,D> u2 // u2會使用一個類型為D的可調用對象來釋放它的指針。 unique_ptr<T,D> u(d) // 空unique_ptr,指向類型為T的對象,用類型為D的對象d代替delete u=nullptr // 釋放u指向的對象,將u置為空 u.release() // u放棄對指針的控制,將u置為空 u.reset() //釋放u指向的對象 u.reset(q) //如果提供了指針q,將u指向這個對象,否則將u置為空。 u.reset(nullptr)
weak_ptr:
weak_ptr是一種不控制所指向對象生存期的智能指針,它指向一個由shared_ptr管理的對象,將一個weak_ptr綁定到一個shared_ptr不會改變shared_ptr的引用計數。一旦最后一個指向對象的shared_ptr被銷毀,對象就會被釋放,即使有weak_ptr指向對象,對象還是會被釋放。
weak_ptr<T> W // 空weak_ptr 可以指向為T的對象 weak_ptr<T> W(sp) //與shared_ptr sp 指向相同對象 weak_ptr w = p // P可以是share_ptr 或weak_ptr w.reset() //w 為空 w.use_count //與w共享對象的shared_ptr豎向 w.expired() //w.use_count() 為0 ,返回true,否則返回為false w.lock() //如果expired 為true,則返回一個空shared_ptr,否則返回一個指向w對象的shared_ptr
weak_ptr 舉例
#include <iostream> #include <memory> #include <vector> using namespace std; class Test{ public: Test(int d = 0) : data(d){cout << "new" << data << endl;} ~Test(){cout << "del" << data << endl;} void func(){cout << "func" << endl;} private: int data; }; class teacher{ public: teacher(){cout << "teacher()" << endl;} ~teacher(){cout << "del teacher" << endl;} shared_ptr<student> stu; }; class student{ public: student(){cout << "student()" << endl;} ~student(){cout << "del student" << endl;} //如果換成shared_ptr<teacher> tea;就會形成循環引用,導致內存泄漏 weak_ptr<teacher> tea; }; int main(){ //用weak_ptr解決了share_ptr循環引用,導致的內存不能釋放的問題 shared_ptr<teacher> tptr(new teacher);//計數器1 shared_ptr<student> sptr(new student);//計數器1 tptr->stu = sptr;//sptr的計數器2 sptr->tea = tptr;//不增加tptr的引用計數,因為tea是weak指針,如果tea是shared_ptr容易導致循環引用 cout << tptr.use_count() << endl;//1 cout << sptr.use_count() << endl;//2 return 0; }
std::move
類型轉換static_cast<typename remove_reference<T>::type &&>(t);
std::move語句可以將左值變為右值而避免拷貝構造。將對象的狀態或者所有權從一個對象轉移到另一個對象。
左值:放在等號左邊的值,可以被賦值: a; ++a; *a;**a;a.m;a->m;a[m]
右值:放在等號右邊的值,不能被賦值;a+b; a++; a&&b; a<b; &a
深拷貝:A先申請出一片新的空間,完全復制B的內容到新空間中;
淺拷貝:A復制B指針,將自己的指針指向B的內容地址,A、B公用一塊內存地址;
A = std::move(B); A 對 B 實現淺層拷貝,為了防止B的改變影響到A ,對B清除。
參考:https://www.lanindex.com/stdmove%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8%E5%88%86%E6%9E%90/
舉例:
#include <iostream> #include <utility> #include <vector> #include <string> int main() { std::string str = "Hello"; std::vector<std::string> v; //調用常規的拷貝構造函數,新建字符數組,拷貝數據 v.push_back(str); std::cout << "After copy, str is \"" << str << "\"\n";//str is "hello" //調用移動構造函數,掏空str,掏空后,最好不要使用str v.push_back(std::move(str)); std::cout << "After move, str is \"" << str << "\"\n";//str is "" std::cout << "The contents of the vector are \"" << v[0] << "\", \"" << v[1] << "\"\n"; }
