我有一种缓冲类,它将std :: vector作为构造函数参数,并将此向量用于存储字节。该类具有读取和写入缓冲区的方法。但是我想给这个缓冲类提供一个const std :: vector,并且仍然能够使用read函数,而write函数在编译时应该失败或至少抛出异常。
我提出的解决方案只有这样:
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:
};
答案 0 :(得分:1)
我不确定设计,但我们可以尝试使用类似于你所要求的东西(这是一个好主意还是不是另一个讨论)。
第一件事是引用不能为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
指针。您可以使用if (writeBuffer)
函数中的write
对其进行测试。请注意,read
应标记为const
函数,因为它不会修改缓冲区。
话虽如此,你仍然需要在设计上更多地工作。声明的read
和write
函数可能还不够。应该读/写什么?第一个/最后一个值?他们应该追加/消费数据(在这种情况下,read
和readBuffer
都不应该是const
)...
答案 1 :(得分: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 :(得分:0)
Const casts在语言中是有原因的。它们应该谨慎使用,但我认为这是其中一种情况。我会这样做(没有语法检查):
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
}
请注意在const
方法中添加了read()
。如果希望编译器另外强制执行const正确性,请构造一个只读缓冲区,如下所示:
const Buffer* buffer = new Buffer(vec);
这使得它只允许调用const
方法。如果没有编写两个完全独立的类,这就像你一样安全。