Feeds:
文章
留言

Archive for 一月 30th, 2008

繼[1]之後,又繼續嘗試了 object_pool ,這個比較符合我的預期和需要,他能夠正常的解構物件,也會自動釋放配置的記憶體相當的棒。object_pool 從 pool 繼承下來,並加上物件管理機制,應該可以達到我的自動解構目的。

改寫[1] 的範例如下:

#include <vcl.h>
#include <iostream>
#include <vector>
#include <boost/pool/object_pool.hpp>
#pragma hdrstop
#include "Unit2.h"
//—————————————————————————

#pragma argsused
class A{
public:
    A(int id_){
        id =id_;
        std::cout << "A()" << id << std::endl;
    }

    ~A(){
        std::cout << "~A()" << id << std::endl;
    }
private:
    int id;
};

typedef boost::object_pool<A> APool;
APool& GetAPool(){
    static APool pool;
    return pool;
}

A* Create(int id){

    return GetAPool().construct(id); // 物件建構
}

int main(int argc, char* argv[]){
    using std::vector;
    vector<A*> aCol;
    aCol.push_back( Create(1) );
    aCol.push_back( Create(2) );
    for(vector<A*>::iterator iter = aCol.begin(); iter!=aCol.end(); ++iter){
//        delete *iter;
        GetAPool().destroy(*iter); // 觸發 ~A()
    }
    A* a = Create(3);
    GetAPool().destroy(a); // 觸發 ~A()

    aCol.clear();

    return 0;
}// ~object_pool 自動解構所有物件

從 object_pool 原始碼來看,destroy() 多了解構的程式,如下:

void destroy(element_type * const chunk)
{
  chunk->~T(); // 呼叫型別解構子
  free(chunk); // 釋放記憶體
}

在 ~object_pool 也會迭代呼叫每一個維護的 ~T(),如下:

template <typename T, typename UserAllocator>
object_pool<T, UserAllocator>::~object_pool()
{
  // handle trivial case
  if (!this->list.valid())
    return;

  details::PODptr<size_type> iter = this->list;
  details::PODptr<size_type> next = iter;

  // Start ‘freed_iter’ at beginning of free list
  void * freed_iter = this->first;

  const size_type partition_size = this->alloc_size();

  do
  {
    // increment next
    next = next.next();

    // delete all contained objects that aren’t freed

    // Iterate ‘i’ through all chunks in the memory block
    for (char * i = iter.begin(); i != iter.end(); i += partition_size)
    {
      // If this chunk is free
      if (i == freed_iter)
      {
        // Increment freed_iter to point to next in free list
        freed_iter = nextof(freed_iter);

        // Continue searching chunks in the memory block
        continue;
      }

      // This chunk is not free (allocated), so call its destructor
      static_cast<T *>(static_cast<void *>(i))->~T();
      // and continue searching chunks in the memory block
    }

    // free storage
    UserAllocator::free(iter.begin());

    // increment iter
    iter = next;
  } while (iter.valid());

  // Make the block list empty so that the inherited destructor doesn’t try to
  //  free it again.
  this->list.invalidate();
}

 

所以,如果你想要大量配置物件,而又想要他自動解構回收,就用 object_pool 吧~

參考資料
[1] Chui-Wen Chiu, "C++ boost::singleton_pool 會自動釋放記憶體但不會呼叫解構"

Read Full Post »

最近想利用 boost 改善程式的穩定性和效能,於是測試 pool 發現一個問題,以下是 CB6 + boost 1.35 寫的測試程式

#include <vcl.h>
#include <iostream>
#include <vector>
#include <boost/pool/singleton_pool.hpp>
#pragma hdrstop
#include "Unit2.h"
//—————————————————————————

#pragma argsused
class A{
public:
    A(int id_){
        id =id_;
        std::cout << "A()" << id << std::endl;
    }

    ~A(){
        std::cout << "~A()" << id << std::endl;
    }
private:
    int id;
};

typedef boost::singleton_pool<A, sizeof(A)> APool;
A* Create(int id){
    void* t = APool::malloc();
    return new (t) A(id);
}

int main(int argc, char* argv[]){
    using std::vector;
    vector<A*> aCol;
    aCol.push_back( Create(1) );
    aCol.push_back( Create(2) );
    for(vector<A*>::iterator iter = aCol.begin(); iter!=aCol.end(); ++iter){
//        delete *iter; // 觸發 ~A()
//        APool::free(*iter); // 不會觸發 ~A()
    }
    aCol.clear();

    A* a = Create(3);
    APool::free(a);// 不會觸發 ~A()

    APool::purge_memory();  // 不會觸發 ~A()

    return 0;
}

我原以為呼叫 free() 或 purge_memory() 會自動呼叫解構並釋放記憶體。但測試結果,只有在明確 delete 時才會觸發解構子

追蹤一下原始碼,當呼叫 purge_memory() 時,實際上他是呼叫 bool pool<UserAllocator>::purge_memory(),此部份的程式碼摘錄如下:

template <typename UserAllocator>
bool pool<UserAllocator>::purge_memory()
{
  details::PODptr<size_type> iter = list;

  if (!iter.valid())
    return false;

  do
  {
    // hold "next" pointer
    const details::PODptr<size_type> next = iter.next();

    // delete the storage
    UserAllocator::free(iter.begin()); // 釋放記憶體

    // increment iter
    iter = next;
  } while (iter.valid());

  list.invalidate();
  this->first = 0;

  return true;
}

從原始碼可看出他只有釋放配置的記體區塊,卻沒有自動呼叫解構子。

所以,使用 pool 或 singleton_pool 只能保證記憶體會完整的釋放,卻無法保證物件完整的解構~

因此, pool 和 singleton_pool 我覺得比較適合用於單純的記憶體配置,而非物件的建構 & 解構

Read Full Post »