2011-06-13 87 views
1

我有一個具有2個純虛擬方法的類和需要使用此類對象的另一個類。我想讓這個類的用戶指定應該在哪個派生類中使用抽象類。 我正在努力弄清楚正確的方法。在另一個類中使用抽象類對象的C++設計模式

struct abstract { 
    virtual int fst_func() = 0; 
    virtual void sec_func(int) = 0; 
}; 

// store an instance of "abstract". 
class user_of_abstract 
{ 
private: 
    abstract* m_abstract; 

public: 
    // Pass a pointer to an "abstract" object. The caller takes care of the memory resource. 
    user_of_abstract_base(abstract* a) : m_abstract(a) { } 

    // Pase any type, which needs to be derived from "abstract" and create a copy. Free memory in destructor. 
    template<class abstract_type> 
    user_of_abstract_base(abstract_type const& a) : m_abstract(new abstract_type(a)) { } 

    // use the stored member to call fst_func. 
    int use_fst_func() { 
     return this->m_abstract->fst_func(); 
    } 

    // use the stored member to call sec_func. 
    void use_sec_func(int x) { 
     this->m_abstract->sec_func(x); 
    } 
}; 

// use boost::shared_ptr 
class user_of_abstract 
{ 
private: 
    boost::shared_ptr<abstract> m_abstract; 

public: 
    // Pass a pointer to an "abstract" object. The caller takes care of the memory resource. 
    user_of_abstract_base(boost::shared_ptr<abstract> a) : m_abstract(a) { } 

    // use the stored member to call fst_func. 
    int use_fst_func() { 
     return this->m_abstract->fst_func(); 
    } 

    // use the stored member to call sec_func. 
    void use_sec_func(int x) { 
     this->m_abstract->sec_func(x); 
    } 
}; 

// pass a pointer of an "abstract" object wherever needed. 
struct user_of_abstract 
{ 
    // use the passed pointer to an "abstract" object to call fst_func. 
    int use_fst_func(abstract* a) { 
     return a->fst_func(); 
    } 

    // use the passed pointer to an "abstract" object to call sec_func. 
    void use_sec_func(abstract* a, int x) { 
     a->sec_func(x); 
    } 
}; 

要注意從sec_func該參數的「X」()是很重要的必須是同一個「抽象」的實例()由fst_func返回的值。

編輯: 增加了另一種方法使用boost :: shared_ptr應該採取最大的優勢。

+1

我認爲你的第二類的名字應該是'user_of_abstract_base'。 – 2011-06-13 05:50:42

+0

不要忘了你需要一個虛擬的析構函數來抽象! – 2011-06-13 05:56:20

+0

@Billy |是的,謝謝你的提示。我已經縮短了相關部分的代碼。 – 0xbadf00d 2011-06-13 06:06:43

回答

1

我要說的是,經過abstract對象插入到用戶的構造函數是用戶的方法取決於被稱爲同abstract對象的正確方法。我甚至會走得更遠,並且使x參數成爲用戶的內部狀態,因爲您已經說過,重要的是該值是從第一個函數調用返回的值。

更新:如果您擔心生命期,那麼您可以利用各種智能指針選項,例如提升。這些應該覆蓋大多數使用場景。

+0

這是正確的,但您需要確保傳入的實例至少和「user_of_abstract」對象具有相同的生命週期,這是我唯一擔心的,另外,它不是那麼方便,因爲你需要兩次持有相同的實例。在「user_of_abstract」對象之外的「抽象」實例。關於內部狀態:請注意我對Cem Kalyoncu的評論。 – 0xbadf00d 2011-06-13 06:14:43

+0

@ FrEEzE2046更新了我的答案。 – ChrisWue 2011-06-13 07:13:36

+0

您是否看到我最初的帖子的更新?我已經展示了boost :: shared_ptr的解決方案;) – 0xbadf00d 2011-06-13 07:29:31

1

既然你說第二個函數應該使用第一個的輸出。我想第一種方法會減少錯誤的機會。你甚至可以將它修改爲以下內容:

int use_fst_func() { 
    return x=this->m_abstract->fst_func(); 
} 

void use_sec_func() { 
    this->m_abstract->sec_func(x); 
} 

protected: 
    int x; 
+0

我不能這樣做,因爲fst_func和sec_func被多次調用,並且fst_func的結果並不總是相同的(實際上,它永遠不會返回兩次相同的數字)。但我有一個std :: set <>來存儲這些信息。 – 0xbadf00d 2011-06-13 06:10:16

+0

如果你的應用程序沒有速度問題,你可以在這個類中有另一個集合,如果提交了一個非獲取值,你可以引發一個異常。這將節省大量的調試時間。此外,只要在調試模式下執行此操作,並且在發佈時不會執行任何檢查,您也可以將此檢查條件化。 – 2011-06-13 06:14:11

+0

我想你已經誤解了我。我使用的std :: set正在做這種工作。抱歉不清楚。 – 0xbadf00d 2011-06-13 06:18:20

0

你讓自己陷入了維護困境。

在你的第一個例子...

真的沒有必要爲模板的構造函數。它speced作爲

// Parse any type, which needs to be derived from "abstract" and create a copy. 

用戶已經可以做到通過創建實例自己,並把它傳遞給第一個構造函數。

另外,在本:

// Free memory in destructor. 

你明確地說,你不知道這個類應該如何使用。在編寫第一個示例時,您需要確定:使用從外部創建的實例或使用在內部創建的實例。看到一個接口與一個ctor接受一個指針,另一個ctor接受一個引用,這兩個接口都是混淆的,基本上都是相同的類型。

在我看來,使用從外部創建的實例的唯一可接受的方式是不存儲管理的從內部創建的將由內存管理的實例是當存在默認的ctor可以初始化內部指針,以一個合理的值(但似乎並不在這裏是如此,因爲要複製另一個實例):

template <typename T> 
class user_of_abstract 
{ 
    bool m_owner_; 
    abstract* m_abstract; 

public: 

    user_of_abstract_base(abstract* a = NULL) 
    : m_owner(a == NULL) 
    , m_abstract(m_owner ? new T(): a) 
    { 
    } 

    ~user_of_abstract_base() 
    { 
     if (m_owner) 
     { 
      delete m_abstract; 
     } 
    } 
} 

你的第二個例子......

優於第一個,因爲您沒有明確地將內存管理與內存引用混合在一起。你讓shared_ptr隱式做。非常好,就是這樣。

但是,由於您要求use_sec_func必須以use_fst_func的輸出作爲輸入,所以您可以遠離安全的海洋維護問題。

例如,如果實例上的use_fst_func引發異常,並且稍後在同一實例上調用use_sec_func會發生什麼情況?

您如何看待重要信息「在B之前始終呼叫A並且只有一次,並將A結果傳遞給B」。應該從現在起2年向班級的用戶傳播?

爲什麼不能use_sec_func只需撥打use_fst_func

至於你的第三個例子...

你可以給1分單的情況,當你想使用這個,而不是僅僅直接調用該實例的功能呢?

+0

與第三個示例相關:這就是std :: allocator_traits <>(C++ 0x)的作用...... – 0xbadf00d 2011-06-13 19:19:06

+0

另外:您可以將use_fst_func視爲獲取資源並使用use_fst_func釋放它的內容。我認爲你已經不知道調用release(acquire())或刪除新的int是沒有任何意義的... – 0xbadf00d 2011-06-13 19:28:41

+0

@ FrEEzE2046:*你可以把use_fst_func當作獲取資源的東西,use_fst_func釋放它*哦,天啊,不!絕對不!那裏的例外安全在哪裏? – 2011-06-13 21:09:43