剥离成员函数的正确方法

时间:2013-12-18 10:48:28

标签: c++

我有一个简单的IO类,带有线程安全的getter和setter。 它看起来像这样:

template <class IO>
class LIO : public LThreadSafe
{
    public:
        LIO() {}
        IO getValue()
        {
            this->lock();
            IO ret = value;
            this->unlock();

            return ret;
        }
        void setValue( IO newval )
        {
            this->lock();
            value = newval;
            this->unlock();
        }

        LInput<IO> *toInput()
        {
            return (LInput<IO> *)this;
        }

    private:
        IO value;

某些类需要能够读取和写入这些IO。 而其他人应该只被允许阅读。 所以我创建了一个Input类,我可以将我的IO类转换为

template <class IO>
class LInput : public LThreadSafe
{
    public:
        LInput() {}
        IO getValue()
        {
            this->lock();
            IO ret = value;
            this->unlock();

            return ret;
        }

    private:
        IO value;

这是好习惯吗? 为此创建一个全新的类似乎过分了。

解决方案

我最终得到了多重继承。一旦我对shared_ptr和C ++更加熟悉,就会检查这个帖子。 希望我能更多次按向上箭头......

1 个答案:

答案 0 :(得分:2)

我能想到的,有两种方法。

使用子类

您可以将LInput私有继承自LIO,但将输入方法标记为公开:

template <typename IO>
class LIO : public LThreadSafe
{
public:
    LIO() {}
    LIO(const LIO& in) { /* make a copy for read-only */ }
    LIO(LIO& io) { /* make a copy for write-only */ }

    shared_ptr<LInput<IO>> toInput() const { return make_shared<LInput<IO>>(*this); }
    shared_ptr<LOutput<IO>> toOutput() { return make_shared<LOutput<IO>>(*this); }
};

template <class IO>
class LInput : private LIO
{
public:
    LInput(LIO<IO>* pio) : LIO<IO>(*pio) {}
    using LIO<IO>::getValue;
};

LInput<int> in;
auto v = in.getValue();        // OK

in.setValue(xyz);              // Error: inaccessible
LIO<int>& io = in;                  // Error: inaccessible

再次考虑这个解决方案,你会发现可以复制IO对象是件好事。如果无法复制,则需要在LIO类中包装实现类引用。

如果你无法复制,一个完整的例子就是:

template <typename IO> class LIOImpl {};

template <typename IO> class LInput;
template <typename IO> class LOutput;

template <typename IO> class LIO {
  shared_ptr<LIOImpl<IO> > pimpl_;

public:
  LIO() : pimpl_(make_shared<LIOImpl<IO> >()) {}
  LIO(const LIO &io) : pimpl_(io.pimpl_) {}

  IO getValue() const { ... }

  LInput<IO> toInput() const { return LInput<IO>(*this); }
  LOutput<IO> toOutput() { return LOutput<IO>(*this); }
};

template <class IO> class LInput : private LIO<IO> {
public:
  LInput(const LIO<IO> &io) : LIO<IO>(io) {}
  using LIO<IO>::getValue;
};

template <class IO> class LOutput : private LIO<IO> {
public:
  LOutput(LIO<IO> io) : LIO<IO>(io) {}
  using LIO<IO>::setValue;
};

使用多重继承

C ++ STL iostream使用多重继承方式来实现istreamostream。在hirarchy中提出了约束。这个钻石继承有时被认为是一种不好的做法。

这种下推约束方法避免了钻石继承

您还可以使用 diamond inheritance 使用纯类作为接口:

template <typename IO>
class LInput
{
public:
    virtual IO getValue() const = 0;
};

template <typename IO>
class LOutput
{
public:
    virtual void setValue(const IO& v) const = 0;
};

template <typename IO>
class LIO : public LInput<IO>, public LOutput<IO>, public LThreadSafe
{
public:
    IO getValue() const override { ... }
    void setValue(const IO& v) const override { ... }

    LInput<IO>* toInput() const { return this; }
    LOutput<IO>* toOutput() { return this; }
};

选择您感觉良好的方式,同时尝试将您的界面与实现隔离开来。一旦达到限制,(或者您只是不喜欢它),您可以轻松更改界面而无需重新实现。

顺便说一句,如果您使用的是C ++ 11,则应使用std::lock_guard而不是手写lock/unlock对:

{
    lock_guard<LThreadSafe> lock;
    // ok you have lock now.
} 
// here lock is automatically released