考虑一个分配内存块以供使用的类:
typedef unsigned char byte;
class ByteBuffer
{
byte* const m_begin; // const pointer, non-const data
byte* const m_end; // const pointer, non-const data
byte* m_pData; // a non-const pointer, e.g. write-head
public:
ByteBuffer(size_t byteBufferSize)
: m_begin(new byte[byteBufferSize],
m_end(m_begin + byteBufferSize),
m_pData(m_begin)
{}
~ByteBuffer() { delete[] m_begin; }
};
如果我想维护代码,很高兴确保m_begin
和m_end
为const
,以确保有关容器大小的任何数学运算都是正确的(并且进来的人不会意外地更新边缘,但是缺点是我无法再在构造函数的初始化列表之外的任何地方初始化数据。
std::bad_alloc
可能很少见,我不相信像上面那样打电话给new[]
是个好主意。所以我的问题是:我们如何处理要在类中管理指向内存的const
指针?
我希望构造函数执行与此类似的操作:
: m_begin(nullptr), m_end(nullptr), m_pData(nullptr)
{
// initialize here
// if initialization is successful, set const & non-const ptrs
}
我是否想得太多?将m_begin
设为const unique_ptr
会使所有初始化麻烦都消失吗?
答案 0 :(得分:0)
上面的代码本质上是编写它的正确方法。但是,您可以考虑改用std::vector
,因为您不必显式管理内存。
如果事实上,std::vector
已经定义了begin()
和end()
(将在您的代码中替换m_pData
),并且您实际上并不需要m_end
替换因为您不自己管理内存。
等效项为begin() + capacity()
,但由于元素未在end
之后初始化,因此您无论如何都不应访问它们(从语言的角度来看可能是未定义的行为)。
否则可能会有一些小的改进,例如:
如果您不希望对象是可移动或可复制的,则最好明确指定它:
public:
ByteBuffer(const ByteBuffer &) = delete;
ByteBuffer(ByteBuffer &&) = delete;
ByteBuffer &operator=(const ByteBuffer &) = delete;
ByteBuffer &operator=(ByteBuffer &&) = delete;
或者,您也可以使用std::unique_ptr
,这样就不必自己释放内存。当您的类需要进行多次分配时,或者在构造函数中的分配之后可能引发异常时,这可能会很有用。
最好的解决方案实际上取决于您想对ByteBuffer
类做什么,甚至取决于您是否真的需要这样的类(因为您可能直接使用std::vector
而不是像这样做一个简单的别名:
using ByteBuffer = std::vector<unsigned char>;
事实上,如果您将byte typedef
用于循环和迭代器,auto
甚至可能没有太大用处。
谈到const
,我通常建议您使用它来防止意外更改。但是,有时您需要在销毁某些复杂类的过程中将这些指针重置为nullptr
,例如,可能会有一些相互销毁。
因此,通过具有尊重 SRP (单一责任原则)的简单类,您基本上可以避免此类问题。