CPP(c++) 智能指針


智能指針

參考: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";
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM