天天看點

簡化C++記憶體管理,避免記憶體洩露的…

      C++不像java和c#那樣有垃圾回收機制,是以記憶體洩露是個比較頭疼的問題(誰會忘記寫delete?誰都會!),有幾種緩解之道:

      1)智能指針(推薦boost::shared_ptr)。

      2)真正的垃圾回收,參考http://www.hpl.hp.com/personal/Hans_Boehm/gc/,這個我沒用過,因為據說不實用:)故本文不讨論。

      3)這是從QT中學到的方法,将堆中配置設定的記憶體組成森林,然後删除根結點的時候級聯删除所有子結點。這樣就簡化了記憶體管理:從管理所有記憶體到隻要管理根結點。

      先讨論方法1:

      對于智能指針來說,如果存在互相引用現象,則會導緻記憶體無法釋放,是以使用時務必要規避這種情況。有人不用智能指針而是自己為配置設定的記憶體管理引用計數,這有點類似于重新造輪子,很多情況下是不必要的。

      避免發生互相引用并不那麼困難,隻要遵守這兩條原則:

      1)如果可以把兩個類了解成"整體-部分"關系(如一個是容器,一個是元素),則整體類對象可以持有部分類對象的智能指針,部分類對象不能持有整體類對象的智能指針。整體類對象的生命周期必須真包含部分類對象的生命周期。如果部分類對象中需要儲存整體類對象的引用,可以使用原生指針(這是安全的,因為整體類活得久)、引用(推薦,因為使用友善且更安全,用過就知道)或者weak_ptr(比較安全但效率沒那麼高)。

      2)除此之外,任何對象不持有其他對象的智能指針,與其他對象協作的唯一方式是通過參數傳入其他對象的智能指針。

      但知易行難,沒辦法檢查程式設計時是否遵守了這兩條原則。

      再讨論方法3:

      這種方法的優點是實作簡單,邏輯清晰,效率高。缺點是對異步和closure的支援不好——鬼知道實際調用的時候,指針指向的那貨是否還健在。加入引用計數?哇靠,那還不如直接用智能指針。

      但是當程式中不存在異步調用和closure要求的時候,不失為一種可行的記憶體管理方法——大部分用戶端和基于阻塞IO的服務端一般滿足該條件。即使有異步調用,隻要保證要用到的東東在實際調用前不會被幹掉(比如那些東東是永存的),也可以使用該方法。

      實際程式設計中如果想要使用該方法,需要基礎類庫的支援。不然一個個去寫基礎類庫(如vector)的wrapper,實在很繁瑣。是以這個從QT中學到的方法,基本上也隻能在QT中用了。

      下面給出方法3的一種簡易實作,CascadeElem類是關鍵(爆簡單),其他都是示例代碼哈。

=================================代碼的分割線=============================================

#include <iostream>

#include <vector>

using namespace std;

class CascadeElem     

{

public:     

      CascadeElem(CascadeElem* parent)     

      {

            if (parent != NULL)

            {

                  parent->children_elem_.push_back(this);

            }

      }

      virtual ~CascadeElem()

      {

            std::size_t cnt = children_elem_.size();

            for (std::size_t i = 0; i < cnt; ++i)

            {

                  delete children_elem_[i];

            }

      }

private:     

      std::vector<CascadeElem*> children_elem_;

};

class Child : public CascadeElem

{

public:

      Child(CascadeElem* parent, int num)

            : CascadeElem(parent)

            , num_(num)

      {

      }

      ~Child()

      {

            cout << "~Child(" << num_ << ")"  << endl;

      }

private:

      int num_;

};

class Foo : public CascadeElem

{

public:

      Foo(CascadeElem* parent, int num)

            : CascadeElem(parent)

            , num_(num)

      {

            for (int i = 0; i < num; ++ i)

            {

                  new Child(this, i);                 

            }

      }

      ~Foo()

      {

            cout << "~Foo(" << num_ << ")"  << endl;

      }

private:

      int num_;

};

int main(int argc, char* argv[])

{     

      Foo* root = new Foo(NULL, 0);

      Foo* ptr1 = new Foo(root, 1);

      Foo* ptr2 = new Foo(ptr1, 2);

      Foo* ptr3 = new Foo(ptr2, 3);

      Foo* ptr4 = new Foo(root, 4);     

      delete root;

      return 0;

}

=================================結果的分割線=============================================

~Foo(0)

~Foo(1)

~Child(0)

~Foo(2)

~Child(0)

~Child(1)

~Foo(3)

~Child(0)

~Child(1)

~Child(2)

~Foo(4)

~Child(0)

~Child(1)

~Child(2)

~Child(3)

可見所有new的對象都被級聯删除了。