这是一个混合,可以用c ++完成吗?

时间:2012-01-24 13:59:37

标签: c++ templates inheritance mixins

我有自己的数组类模板,我想可选择添加功能。

作为功能的一个例子,采用多线程支持:在某些情况下,我需要在任何更新代码之前放置#pragma omp atomic的数组(强制执行原子行为的编译器指令,细节并不重要)。在其他情况下,我需要不执行此操作的数组,因为我知道它们只会安全更新,我需要避免性能损失。

直观地说,应该可以定义一个我可以继承的名为AtomicUpdates的类。所以要定义一个带有原子更新的双数组,我会说像

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

但是我看不出你在实践中如何实现它,这也会破坏继承接口的原则,而不是实现

任何人都可以告诉我我在这里真正想做什么吗?

1 个答案:

答案 0 :(得分:5)

即使您最终没有使用它们,现在使用mixins和策略模板参数是非常有用的事情。在这种情况下,它们非常相似。首先,一个带有mixin base的数组。我使用过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的重要区别在于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的优势很有用,但并不常见。因此,在使用模板时,策略方法通常更合适。标准库在许多地方使用策略参数,因此有一些很好的例子供您学习。

关于“继承接口,而不是实现”的问题。使用仔细继承实现是非常有用的。多重继承也是如此。你只需要明智地使用它们。