2012-07-24 70 views
1

我會打開說我已經研究了幾天,試圖討論做這件事的「正確方法」是什麼。在大量的關於RAII /游泳池設計/智能指針的搜索引擎上,並沒有得出明確的結論(除了可能沒有絕對的「正確方法」)之外,我想也許是時候讓更有知識的人指點我正確的方向了。需要關於對象/連接池項目的RAII設計的建議

我正在構建一個對象池,並且我試圖確保客戶端代碼可以使用RAII,如果需要的話。

有3個實體參與:

  • 資源。昂貴的構建,半便宜的重用(檢查其發佈狀態不是微不足道的)。由於它使用自己分配的內存/資源包裝了一些C結構,所以很難複製。
  • 包裝/把手。資源包裝器。在ctor上給予資源,在dtor上釋放它。
  • 控制器/池。維護一個資源池,並通過包裝程序或由客戶自行決定直接管理客戶的使用。

我在下面展示了一個我已經想到的簡單例子。在函數DoSomethingElse()中,你可以看到我所追求的 - 我得到一個Wrapper的引用,並在範圍的末尾調用它的dtor,並將資源釋放回池中。

我的問題與Factory::GetResource()的定義有關。這裏介紹的簡化版本每次只分配一個新版本;我的實際實現將檢查池是否有可用資源(如果沒有可用資源,則創建一個),將其標記爲正在使用中,並返回對其的引用。

我寧願避免爲資源定義合適的副本,因此是按引用返回,而不是按值返回。資源保證超出調用者的時間,並且Controller在整個應用程序的整個生命週期中保持所有權 - 它們不會交給用於生命週期管理的客戶端代碼。當然,如果客戶要求提供直接參考,即沒有包裝,則所有投注都關閉。

這個設計是否聽起來?使用shared_ptr會更好嗎?或者其他一些機制/設計?

謝謝你的時間。

#include <iostream> 
#include <vector> 

using namespace std; 

static int seq = 0; // POOR MAN'S SEQUENCE FOR INSTANCE IDs 

class Resource 
{ 
public: 
    Resource() : id(seq++) { cout << "Resource ctor: " << id << endl; } 
    ~Resource() { cout << "Resource dtor: " << id << endl; } 
private: 
    int id; 
}; 

class Wrapper 
{ 
public: 
    // ON ACTUAL IMPLEMENTATION, NOTIFY THE CONTROLLER OF THE RELEASE 
    ~Wrapper() 
     { cout << "Wrapper dtor: " << id << "Welease Bwian! Ee, I mean, the wesouwce" << endl; } 

    explicit Wrapper(Resource& r) : id(seq++), res(r) 
     { cout << "Wrapper ctor: " << id << endl; } 

    int getID() const { return id; } 
private: 
    int id; 
    Resource& res; 
}; 

class Controller 
{ 
public: 
    ~Controller() { for (auto r : allres) delete r; } 
    Resource& GetResource(); 
private: 
    // SIMPLIFIED. I'M USING Boost PTR CONTAINER 
    vector<Resource *> allres; 
}; 

// SIMPLIFIED. IT WOULD ACTUALLY GET A RESOURCE FROM THE POOL 
Resource& Controller::GetResource() 
{ 
    Resource* newres = new Resource(); 
    allres.push_back(newres); 

    return *(newres); 
} 

// SIMULATE GLOBAL CONTEXT 
Controller& GetController() 
{ 
    static Controller f; 
    return f; 
} 

void DoSomething(Wrapper& wr) 
{ 
    cout << "DoSth INI" << endl; 
    cout << wr.getID() << endl; 
    cout << "DoSth END" << endl; 
} 

void DoSomethingElse() 
{ 
    cout << "DoSthElse INI" << endl; 
    Wrapper w(GetController().GetResource()); 
    DoSomething(w); 
    cout << "DoSthElse END" << endl; 
} 

int main(int argc, char *argv[]) 
{ 
    cout << "main INI" << endl; 
    cout << "Calling DoSthElse" << endl; 
    DoSomethingElse(); 
    cout << "Called DoSthElse" << endl; 
    cout << "main END" << endl; 
} 
+0

您的編譯器是否支持右值引用?實現移動語義,使其更容易處理不可複製的對象 – jalf 2012-07-24 19:59:15

+0

另外,爲什麼還要暴露資源和包裝?所有這些都可以爲錯誤留出更多空間。只需公開一個單一的RAII資源類。它不應該由呼叫者來包裝它。爲什麼不給用戶一個行爲良好的RAII資源類? – jalf 2012-07-24 20:05:23

+0

@jalf:是的,它支持它。你建議我將它應用於資源?所以,當一個資源被使用時,我實際上將它從容器中移除並將其移動到包裝器/客戶端?我有錯了嗎?我已經考慮過這一點,但這聽起來並不合適。所有權似乎總是在控制器中。至於揭露資源,我知道你的意思。我並不完全滿意,我可能會隨着它的進展而刪除它。 – PCaetano 2012-07-24 20:45:47

回答

2

RAII真的是所有權。誰擁有這個對象,一旦他們放棄了它的所有權,他們需要做什麼?

您所描述的情況是Controller所擁有的資源真的是擁有。資源對象的生命週期由Controller管理。

資源的用戶只是「鎖定」資源,將其標記爲「正在使用」,但他們並不承擔責任。他們不會影響其一生。 (你可以說他們擁有一個鎖,然後是,他們需要管理的資源)

所以我建議露出有點像std::unique_ptr<Resource>,其與定製刪除創建的。 (和他們喜歡,可以通過值從controller.getResource()通話

用戶可以與此unique_ptr做返回什麼:這不是可複製的,但它可以移動,一旦超出範圍,它所謂的定製刪除,在Controller中將其標記爲「未使用」,將其有效地返回給池

通過這種方式,您可以通過值返回對象,這對於客戶端來說非常簡單並且您可以避免暴露「解包「資源對象:客戶端總是讓它們包裝在一個unique_ptr中,這消除了很多潛在的錯誤。

1

注意,在當前的代碼沒有對當呼叫者使用包裝/資源完成的控制器告訴方式。這意味着,當您開始實施Controller :: GetResource時,控制器無法知道它是否可以返回以前創建的資源。

像這樣的設計通常包含一個在Wrapper析構函數中調用的Controller :: ReleaseResource。這意味着當構建包裝器時,它獲取資源,然後在它被破壞時釋放資源。這正是RAII。

+0

對不起,當我說這是一個簡單的例子時,我應該更加明確。實際的代碼實現了這一點。我已經爲'Wrapper ::〜Wrapper'添加了一條評論來說明問題。感謝您的幫助。 – PCaetano 2012-07-24 20:41:29