(原創)c++11改進我們的程序之垃圾回收


  c#和java中有自動垃圾回收機制,.net運行時和java虛擬機可以管理分配的堆內存,在對象失去引用時自動回收,因此在c#和jva中,
內存管理不是大問題。c++語言沒有垃圾回收機制,必須自己去釋放分配的堆內存,否則就會內存泄露。
  我相信大部分c++開發人員都遇到過內存泄露的問題,而查找內存泄露的問題往往要花大量的精力。要解決這個讓人頭疼的問題可
以采取一些辦法,最有效的辦法是使用智能指針!使用智能指針就不會擔心內存泄露的問題了,因為智能指針可以自動刪除刪除分
配的內存。

  智能指針是指向動態分配(堆)對象指針,用於生存期控制,能夠確保自動正確的銷毀動態分配的對象,防止內存
泄露。它的一種通用實現技術是使用引用計數。每次使用它,內部的引用計數加1,每次析構一次,內部引用計數減1,減為0時,刪
除所指向的堆內存。

c++11之前,c++沒有內置智能指針,之前只能借助於boost的智能指針或者自己發明輪子。c++11現在內置的智能指針,使我們可以
很方便的使用智能指針了。c++11中的智能指針包括:

  • std::shared_ptr
  • std::unique_ptr
  • std::weak_ptr

std::shared_ptr

std::shared_ptr使用引用計數. 每一個shared_ptr的拷貝都指向相同的內存。 在最后一個shared_ptr析構的時候, 內存才會被釋
放。
我們看看shared_ptr如何使用吧。
1.初始化

//智能指針的初始化
std::shared_ptr<int> p(new int(2));
std::shared_ptr<int> p2 = p;
std::shared_ptr<BaseConnector> m_connt = make_shared<Connector>(m_ios, m_strIP, m_port);

通過構造函數、賦值函數或者make_shared函數初始化智能指針。
智能指針初始化可以指定刪除器

void DeleteIntPtr(int* p)
{
delete p;
}

std::shared_ptr<int> p(new int, DeleteIntPtr);
p的引用計數為0時會自動調用刪除器DeleteIntPtr

2.智能指針中的原始指針,通過get獲取
char* pData = pBuf.get();

3.注意事項。智能指針雖然能自動管理堆內存,但是它有不少陷阱,使用時需要注意:

1.不要把一個原生指針給多個shared_ptr管理

int* ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); //logic error
導致ptr被刪除兩次

2.不要把this指針給shared_ptr
3.不要在函數實參里創建shared_ptr
function ( shared_ptr<int>(new int), g( ) ); //有缺陷
可能的過程是先new int,然后調g( ),g( )發生異常,shared_ptr<int>沒有創建,int內存泄露
shared_ptr<int> p(new int());
f(p, g()); 
4.對象內部生成this的shared_ptr  

enable_shared_from_this 類,在該類中定了成員函數 shared_from_this() ,返回shared_ptr<T> 。這個函數僅在 shared_ptr<T> 的構造函數被調用之后才能使用。原因是 enable_shared_from_this::weak_ptr 並不在構造函數中設置(此處的構造函數指的是類型 T 的構造函數),而是在 shared_ptr<T> 的構造函數中設置(此處的構造函數指的是類型 shared_ptr<T> 的構造函數)。

若不使用 shared_from_this() 成員函數,將this指針構造了一個shared_ptr,外面創建的對象本身的shared_ptr也管理了this資源,在析構時則會發生兩次析構;

class Y: public std::enable_shared_from_this<Y>
{
boost::shared_ptr<Y> GetSelf()
{
return shared_from_this();
}
};

boost::shared_ptr<Y> spy(new Y)
boost::shared_ptr<Y> p = spy->GetSelf(); //OK

5.shared_ptr作為對象的成員時,小心因循環引用造成無法釋放資源。

struct A;
struct B;
struct A
{
std::shared_ptr<B> m_b;
};

struct B
{
std::shared_ptr<A> m_a;
};

std::shared_ptr<A> ptrA(new A);
std::shared_ptr<B> ptrB(new B);
ptrA->m_b = ptrB;
ptrB->m_a = ptrA;

ptrA和ptrB相互引用,離開作用域時引用計數都為1,導致內存沒有被釋放,解決辦法是把A和B任何一個的成員變量改為weak_ptr
解決辦法是把A和B任何一個的成員變量改為weak_ptr

struct B
{
std::weak_ptr<A> m_a;
};

ptrB->m_a不會增加A對象的引用計數,因此A對象離開作用域時,引用計數為0,m_b的引用計數減一,b離開作用域后引用計數由1減為0.

std::weak_ptr
弱引用指針,用來監視智能指針,不會使引用計數加1。在訪問所引用的對象前必須先轉換為 std::shared_ptr。

用來表達臨時所有權的概念:當某個對象只有存在時才需要被訪問,而且隨時可能被他人刪除時,可以使用

來跟蹤該對象。需要獲得臨時所有權時,則將其轉換為 std::shared_ptr,此時如果原來的std::shared_ptr 被銷毀,則該對象的生命期將被延長至這個臨時的 std::shared_ptr 同樣被銷毀為止。

還可以用來避免 std::shared_ptr 的循環引用。

std::weak_ptr<int> gw; 
void f()
{
if (auto spt = gw.lock()) { // Has to be copied into a shared_ptr before usage
std::cout << *spt << "\n";
}
else {
std::cout << "gw is expired\n";
}
}

int main()
{
{
auto sp = std::make_shared<int>(42);
gw = sp;
f();
}
f();
}
輸出
42 
gw is expired

 std::unique_ptr

unique_ptr不會共享它的指針。 無法將它復制到另一個unique_ptr, unique_ptr只能移動。 這意味着內存資源的所有權將轉移到新的unique_ptr和原始unique_ptr不再擁有它。
int* p = new int;
std::unique_ptr<int> ptr(p);
std::unique_ptr<int> ptr1 = ptr; //不能復制,編譯報錯

auto ptr2 = std::move(ptr); //轉移所有權, 現在ptr那塊內存歸ptr2所有, ptr成為無效的指針.

 

智能指針是個好東西,使用它之后就不用擔心內存釋放、內存泄露的問題了,我的項目中都是用的智能指針,沒有delete。

智能指針使我們的程序更安全,除了循環引用會導致內存泄露之外,其他都很安全,可以放心使用。

 

c++11 boost技術交流群:296561497,歡迎大家來交流技術。


免責聲明!

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



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