2012-01-24 5 views
5

私は独自の配列クラステンプレートを持っています。オプションで機能を追加したいと思います。これはミックスインですか、それはC++でできますか?

機能の例として、マルチスレッドのサポートがあります。場合によっては、更新コードの直前に#pragma omp atomicという配列が必要です(アトミックな動作を実行するコンパイラ指令です。詳細は重要ではありません)。他のケースでは、これをしない配列が必要です。私はそれらが安全に更新されるだけで、パフォーマンスが低下するのを避ける必要があることを知っています。

私が継承できるAtomicUpdatesというクラスを直感的に定義することは可能です。だから私は

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

のようなものを言うでしょう。しかし、私はあなたが実際にそれを実装したいどのように見ることができない、また、これは継承インターフェイスではなく、実装の原則を破る原子更新のダブル配列を定義するには

私が本当にここでやりたいことについて誰でも教えてもらえますか?

+2

「MyArray」テンプレートにポリシーパラメータを追加するのが好きかもしれません。 –

答えて

5

mixinsとポリシーテンプレート引数を使用しなくても、今すぐ理解するのは非常に便利なことです。この場合、彼らは非常に似ています。まず、mixinベースの配列。 OpenMPではなくC++ 0xのミューテックスを使用しましたが、そのアイデアを得るべきです。

#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"; } 
}; 

ミックスインの利点は、その多くの場合ではありません便利ですいくつかの実際の例がありますが。だから、テンプレートを使って作業するときには、ポリシーアプローチがしばしば適切です。ポリシーの引数は多くの場所で標準ライブラリによって使用されているので、あなたが勉強する良い例がいくつかあります。

"継承インターフェイス、実装ではありません。使用を慎重に継承することは非常に便利です。複数の継承にも同じです。あなたはそれらをいつ使うべきかについて賢明である必要があります。

+1

別の重要な違いがあります。つまり、最初のコードだけがコンパイラでEBO空の基本最適化を実行できるようになります。多くのポリシークラスにはデータメンバーがないため、通常、これにより派生クラスが小さくなるため、後続のデータメンバーのメモリアライメントが向上する可能性があります。これは、標準ライブラリの実装やBoostなどのライブラリのほとんどの(すべての)ポリシーが、仮想関数を使用していなくても非公開で継承される理由です。 –

+0

+1日常の参照+1 – bronekk

+0

ありがとう、それははるかに明確です。しかし、なぜそのポリシーアプローチは仮想メソッドも定義できないのですか? –

関連する問題