【问题标题】:Read only version of class for const parameterconst 参数类的只读版本
【发布时间】:2012-08-17 21:29:44
【问题描述】:

我有一种缓冲区类,它将 std::vector 作为构造函数参数并使用此向量来存储字节。此类具有读取和写入缓冲区的方法。但是我想给这个缓冲区类一个 const std::vector 并且仍然能够使用读取函数,而写入函数应该在编译时失败或至少抛出异常。

我想出的唯一解决方案是这样的:

class Buffer
{
    public:
    Buffer(std::vector<uint8>& vec)
    { 
        this->writeVec = vec
        this->readVec = vec;
    }
    Buffer(std::vector<uint8> const& vec)
    {
        this->writeVec = null
        this->readVec = vec;
    }
    void write(uint8 i)
    {
        this->throwIfWriteVecNull();
        // do write to writeVec
    }
    uint8 read()
    {
        // do read from readVec
    }

    private:
    std::vector<uint8>& writeVec;
    std::vector<uint8> const& readVec;
}

是否有任何方法可以在没有单独的编写器和读取器类的情况下实现这一点(它将相似的逻辑分离到两个不好的不同类)并且编译时检查写访问?我不想将 const_casts 用于任何其他不安全的黑客攻击。也可以随意提出替代模式/架构作为解决方案。

编辑:

感谢大家的回答。从三个答案中,user315052 的外观模式最接近我想要的东西,因为 IMO const_cast 或多个指针比拥有更多类更糟糕。我还做了更多的研究,偶然发现了this SO q/a,其中模板用于在 const 和非 const 类型之间进行选择。现在我有类似以下的东西并且它工作完美,如果我尝试在 const 版本上调用 write,我会在编译时得到“无方法错误”。由于所有模板和东西,代码有点难看,但在编译时出错比异常要好得多。

template <typename BlockType, bool isMutable>
class BaseBuf : boost::noncopyable
{
public:
    typedef typename boost::mpl::if_c<isMutable, std::vector<BlockType>, std::vector<BlockType> const>::type VectorType;
    BaseBuf(VectorType& blocks) : blocks(blocks)
    {
    }
    void seekReadCursor(size_t pos)
    {
         // seek to pos
    }
    bool readBool() const
    {
         // do read from pos
    }
    //void read...
    //...
protected:

    VectorType& blocks;
};

template <typename BlockType>
class ImmuBuf : public BaseBuf<BlockType, false>
{
public:
    typedef BaseBuf<BlockType, false> Parent;
    typedef typename Parent::VectorType VectorType;
    ImmuBuf(VectorType& blocks) : Parent(blocks)
    {
    }
private:

};

template <typename BlockType>
class MutaBuf : public BaseBuf<BlockType, true>
{
public:
    typedef BaseBuf<BlockType, true> Parent;
    typedef typename Parent::VectorType VectorType;
    MutaBuf(VectorType& blocks) : Parent(blocks)
    {
    }
    // void resize()...
    void writeBool(bool b)
    {
        // do write
    }
    //void write...
    //...
private:

};

【问题讨论】:

  • 如果将read() 方法指定为const 并且向量只有一个内部成员呢?
  • 不要害怕创建两个类。一个可以轻松地从另一个继承,或者像 user315052 的答案那样共享通用代码。也就是说,一个类可能就足够了——这取决于它的使用方式。

标签: c++ constants


【解决方案1】:

您似乎真的希望 Buffer 成为读/写版本和只读版本的外观。

class BufferInterface {
    friend class Buffer;
    friend class std::default_delete<BufferInterface>;
protected:
    virtual ~BufferInterface () {}
    virtual void write (uint8_t) = 0;
    virtual uint8_t read () = 0;
    //...
};

class Buffer {
    std::unique_ptr<BufferInterface> impl_;
public:
    Buffer (std::vector<uint8_t> &v) : impl_(new BufferReadWrite(v)) {}
    Buffer (const std::vector<uint8_t> &v) : impl_(new BufferReadOnly(v)) {}
    void write(uint8_t i) { impl_->write(i); }
    uint8_t read () { return impl_->read(); }
    //...
};

BufferInterface 可以实现任何通用逻辑,供只读版本和读/写版本重用。

【讨论】:

    【解决方案2】:

    我不确定设计,但我们可以尝试按照您的要求进行类似的工作(是否是一个好主意是另一个讨论)。

    第一件事是引用不能为NULL,如果你想提供可选参数,那么你应该使用指针或更高级别的构造(boost::optional)。然后可以提供多个构造函数:

    class Buffer {
       std::vector<uint8_t> const *readBuffer;
       std::vector<uint8_t>       *writeBuffer;
    public:
       Buffer( std::vector<uint8_t>& v ) : readBuffer(&v), writeBuffer(&v) {}
       Buffer( std::vector<uint8_t> const & v ) : readBuffer(&v), writeBuffer() {}
       void write( uint8_t v );
       uint8_t read() const;
    };
    

    如果传递给 Buffer 的参数是 const,则不会设置 writeBuffer 指针。您可以通过在 write 函数中使用 if (writeBuffer) 来测试它。请注意,read 应标记为 const 函数,因为它不会修改缓冲区。

    话虽如此,您仍然需要在设计上做更多的工作。声明的 readwrite 函数可能还不够。应该读/写什么?第一个/最后一个值?他们是否应该附加/使用数据(在这种情况下,readreadBuffer 都不应该是 const)...

    【讨论】:

    • 感谢指出错误!我应该像你说的那样使用指针!我的“真实”类包含类似 20 种用于不同大小和字节序的整数的读写方法。相应的 write 和 read 方法共享有关它们处理的数据类型的逻辑,这就是我不想要单独的 writer 和 reader 类的原因。好的一点是 read() 可以消耗数据!在我的设计中,它不是,因此可以是 const。
    【解决方案3】:

    常量转换在语言中是有原因的。它们应该谨慎使用,但我认为这是其中一种情况。我会这样做(不检查语法):

    Buffer(std::vector<uint8> const& vec)
    {
        this->vec = const_cast<std::vector<uint8>& >(vec);
        this->readonly = true;
    }
    void write(uint8 i)
    {
        this->throwIfReadOnly();
        // do write to vec
    }
    uint8 read() const
    {
        // do read from vec
    }
    

    注意constread() 方法上的添加。如果您希望编译器额外强制执行 const 正确性,请像这样构造一个只读缓冲区:

    const Buffer* buffer = new Buffer(vec);
    

    这使得它只允许调用const 方法。这与您无需编写两个完全独立的类一样安全。

    【讨论】:

      猜你喜欢
      • 2023-03-09
      • 1970-01-01
      • 2019-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多