我目前正在研究一个使用分配器来管理资源的容器类。我将尝试给出我目前调整容器大小的简短版本。(真实的不是一维的,但方案是相同的,因为分配的数据是连续的。)
我不清楚的所有内容都标记为 [[[ x ]]]。
示例代码
template<typename T>
class example
// ...
笔记:
- size_type === std::allocator::size_type
- 指针 === std::allocator::pointer
- _A === std::allocator 的对象
- _begin 是当前容器数据开头的类成员( [_begin,_end) )
- size() 返回 (_end - _begin)
- clear() 为 [_begin,_end) 和 _A.deallocate(_begin,size()) 中的所有元素调用 _A.destroy()
- 析构函数调用 clear()
调整大小的来源(size_t):
void resize (size_type const & new_size)
{
if (new_size == 0U)
{ // we resize to zero, so we just remove all data
clear();
}
else if (new_size != size())
{ // we don't go to zero and don't remain the same size
size_type const old_size = size();
pointer new_mem(nullptr);
try
{
new_mem = _Allocate(new_size);
}
catch (std::bad_alloc e)
{
// [[[ 1 ]]]
}
size_type counter(0);
for (size_type i=0; i<new_size; ++i)
{
try
{
if (i<size())
_A.construct(new_mem + i, const_cast<const_reference>(*(_begin+i)));
// [[[ 2 ]]]
else
_A.construct(new_mem + i);
++counter;
}
catch (...) // [[[ 3 ]]]
{
// [[[ 4 ]]]
}
}
clear();
_begin = new_mem;
_end = _begin + new_size;
}
}
问题:
[[[1]]]
我应该调用 clear() 并在此处重新抛出还是调用当前对象的析构函数,如果我没有在这里捕获?
[[[2]]]
在这里使用 const_cast() 或 std::move() 转换为右值引用怎么样?这个打破异常安全吗?
如果我移动构造,假设 10 个元素中有 9 个,并且元素 10 在移动构造中抛出一些东西,我将失去 10 个对象中的 9 个!?
[[[3]]]
我读到catch (...)
应该避免。不过,我不知道是否还有其他可能。有没有办法在不知道构造函数是否或向我抛出什么的情况下避免使用通用捕获?
[[[4]]]
我相信这里的正确步骤是:
- 通过调用范围 [new_memory, new_memory+counter) 上的析构函数来回滚已完成的构造
- 释放 new_mem
- 调用清除()
- 重新投掷
这个对吗?