2012-01-24 68 views
5

我有我自己的數組類模板我想選擇性地添加功能。這是一個mixin,可以在C++中完成嗎?

隨着功能的示例,採取多線程支持:在某些情況下,我需要把#pragma omp atomic之前的任何更新編碼陣列(即強制原子行爲編譯器指令,細節並不重要)。在其他情況下,我需要不這樣做的數組,因爲我知道它們只會被安全地更新,並且需要避免性能下降。

直覺上應該可以定義一個我可以繼承的名爲AtomicUpdates的類。因此,要定義與原子更新雙陣列我會說,像

class AtomicDoubleArray : public MyArray<double>, public AtomicUpdates {}; 

,但我看不出你如何實現,在實踐中,也這將打破繼承接口的原則,不執行

任何人都可以啓發我,我真的想在這裏做什麼?

+2

聽起來像給我的'MyArray'模板添加策略參數可能會更好。 –

回答

5

即使您現在還沒有最終使用它們,mixins和策略模板參數也是非常有用的東西。在這種情況下,它們非常相似。首先,一個帶有mixin庫的數組。我已經使用C++ 0x互斥而不是openmp,但你應該明白。

#include <iostream> 
#include <vector> 
#include <mutex> 

template <class value_t, class base_t> 
class array_t : private base_t { 
    std::vector<value_t> v_; 
public: 
    array_t(size_t sz = 0) : v_ (sz) { } 
    value_t get(size_t i) const 
    { 
     this->before_get(); 
     value_t const result = v_[i]; 
     this->after_get(); 
     return result; 
    } 
    void set(size_t i, value_t const& x) 
    { 
     this->before_set(); 
     v_[i] = x; 
     this->after_set(); 
    } 
}; 

class no_op_base_t { 
protected: 
    void before_get() const { } 
    void after_get() const { } 
    void before_set() const { } 
    void after_set() const { } 
}; 

class lock_base_t { 
    mutable std::mutex m_; 
protected: 
    void before_get() const { std::cout << "lock\n"; m_.lock(); } 
    void after_get() const { std::cout << "unlock\n"; m_.unlock(); } 
    void before_set() const { std::cout << "lock\n"; m_.lock(); } 
    void after_set() const { std::cout << "unlock\n"; m_.unlock(); } 

}; 

int main() 
{ 
    array_t<double, no_op_base_t> a (1); 
    array_t<double, lock_base_t> b (1); 
    std::cout << "setting a\n"; 
    a.set(0, 1.0); 
    std::cout << "setting b\n"; 
    b.set(0, 1.0); 
    std::cout << "getting a\n"; 
    a.get(0); 
    std::cout << "getting b\n"; 
    b.get(0); 
    return 0; 
} 

現在是相同的類,但使用策略參數方法而不是繼承。

#include <iostream> 
#include <vector> 
#include <mutex> 

template <class value_t, class policy_t> 
class array_t { 
    policy_t policy_; 
    std::vector<value_t> v_; 
public: 
    array_t(size_t sz = 0) : v_ (sz) { } 
    value_t get(size_t i) const 
    { 
     policy_.before_get(); 
     value_t const result = v_[i]; 
     policy_.after_get(); 
     return result; 
    } 
    void set(size_t i, value_t const& x) 
    { 
     policy_.before_set(); 
     v_[i] = x; 
     policy_.after_set(); 
    } 
}; 

class no_op_base_t { 
public: 
    void before_get() const { } 
    void after_get() const { } 
    void before_set() const { } 
    void after_set() const { } 
}; 

class lock_base_t { 
    mutable std::mutex m_; 
public: 
    void before_get() const { std::cout << "lock\n"; m_.lock(); } 
    void after_get() const { std::cout << "unlock\n"; m_.unlock(); } 
    void before_set() const { std::cout << "lock\n"; m_.lock(); } 
    void after_set() const { std::cout << "unlock\n"; m_.unlock(); } 

}; 

int main() 
{ 
    array_t<double, no_op_base_t> a (1); 
    array_t<double, lock_base_t> b (1); 
    std::cout << "setting a\n"; 
    a.set(0, 1.0); 
    std::cout << "setting b\n"; 
    b.set(0, 1.0); 
    std::cout << "getting a\n"; 
    a.get(0); 
    std::cout << "getting b\n"; 
    b.get(0); 
    return 0; 
} 

在這種情況下,兩者都非常相似。最重要的區別是mixin可以將一些方法定義爲虛擬的,並允許您通過繼承來改變數組的行爲。如下所示:

template <class value_t> 
class mk_virtual_base_t { 
protected: 
    void before_get() const { } 
    void after_get() const { } 
    void before_set() const { } 
    void after_set() const { } 

    virtual value_t get(size_t) const = 0; 
    virtual void set(size_t, value_t) = 0; 
}; 

template <class value_t> 
class daily_wtf_contender_t : public array_t<value_t, mk_virtual_base_t<value_t> > { 
    virtual value_t get(size_t) const { std::cout << "surprise! get is virtual!\n"; return 0; } 
    virtual void set(size_t, value_t) { std::cout << "surprise! set is virtual!\n"; } 
}; 

雖然有一些真正的情況下mixin的優點是有用的,但並不經常。因此,在使用模板時,政策方法通常更合適。標準庫在許多地方都使用策略參數,因此您可以學習一些很好的示例。

至於你有關「繼承接口,而不是實現」的問題。謹慎使用繼承實現是非常有用的。多重繼承也是如此。你只需要明白你什麼時候使用它們。

+1

還有一個重要的區別,即只有第一個代碼允許編譯器執行EBO - 空基優化。由於許多策略類沒有數據成員,這通常會使得派生類更小,這可能會提高後續數據成員的內存對齊性。這就是爲什麼即使不使用虛函數,標準庫實現和庫中的大多數(所有)策略都是私有繼承的原因。 –

+0

+1僅供參考每日wtf – bronekk

+0

謝謝,這更清晰。但爲什麼政策方法不能定義虛擬方法呢? –