gpt4 book ai didi

c++ - 如何安全地处理容器类中的异常/异常?

转载 作者:塔克拉玛干 更新时间:2023-11-03 06:50:34 25 4
gpt4 key购买 nike

我目前正在开发一个使用分配器来管理资源的容器类。我将尝试简要介绍一下我目前为调整容器大小所做的工作。 (真实的不是一维的,但方案是相同的,因为分配的数据是连续的。)

所有我不清楚的地方都标记为 [[[ 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
  • 调用clear()
  • 重新抛出

这是正确的吗?

最佳答案

你真的想避免所有的try/catch 东西并使用 RAII 来确保正确的资源清理。例如:

void resize (size_type const & new_size)
{
example<T> tmp(_A); // assuming I can construct with an allocator

// If the allocation throws then the exception can propogate without
// affecting the original contents of the container.
tmp._end = tmp._begin = tmp._A.allocate(new_size);


for (size_type i = 0; i < std::min(size(), new_size); ++i)
{
tmp._A.construct(tmp._begin + i, _begin[i]);
++tmp._end; // construction successful, increment _end so this
// object is destroyed if something throws later
}
for (size_type i = size(); i < new_size; ++i)
{
tmp._A.construct(tmp._begin + i);
++tmp._end; // as above
}

// OK, the copy of old objects and construction of new objects succeeded
// now take ownership of the new memory and give our old contents to the
// temporary container to be destroyed at the end of the function.

std::swap(_begin, tmp._begin);
std::swap(_end, tmp._end);
}

注意事项:

  • 您说“clear()[_begin,_end) 中的所有元素调用 _A.destroy() 并且_A.deallocate(_begin,size())”。为简单起见,我假设 deallocate 并不真正关心 size() 参数,这对某些分配器来说是正确的。如果这很重要,那么您可能希望 example 有一个“容量”的概念和一个 _capacity_end_of_storage 成员。将大小容量 分开将使清理更易于编写且更可靠。

  • 您已经在析构函数(和/或它调用的函数)中编写了正确的清理代码。通过使用临时容器,我可以重用该代码而不必复制它。

  • 通过使用本地对象,我可以避免所有 try/catch block ,并依靠本地对象的自动销毁来清理资源。

关于c++ - 如何安全地处理容器类中的异常/异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16577337/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com