C++ 冒着异常反模式的风险。。经过一些修改

C++ 冒着异常反模式的风险。。经过一些修改,c++,anti-patterns,C++,Anti Patterns,假设我有一个在某些机器上全天候运行的库。即使代码坚如磐石,硬件故障迟早会触发异常。我想为这样的事件提供某种故障保护。一种方法是编写封装每个api的包装器函数: returnCode=DEFAULT; try { returnCode=libraryAPI1(); } catch(...) { returnCode=BAD; } return returnCode; 然后,库的调用者重新启动整个线程,如果返回代码不正确,则重新初始化模块 事情可能会大错特错。例如 如果try块(或libr

假设我有一个在某些机器上全天候运行的库。即使代码坚如磐石,硬件故障迟早会触发异常。我想为这样的事件提供某种故障保护。一种方法是编写封装每个api的包装器函数:

returnCode=DEFAULT;
try
{
  returnCode=libraryAPI1();
 }
catch(...)
{
 returnCode=BAD;
}
return returnCode;
然后,库的调用者重新启动整个线程,如果返回代码不正确,则重新初始化模块

事情可能会大错特错。例如

如果try块(或libraryAPI1())具有:

如果func2()抛出异常,x将永远不会被释放。同样,文件损坏也是一种可能的结果


您能告诉我在这种情况下还有哪些事情可能出错吗?

您可以控制libraryAPI的实现吗

如果它能适应OO模型,您需要使用RAII模式来设计它,这保证了析构函数(谁将释放获得的资源)在异常时被调用

使用资源管理帮助器(如智能指针)也会有帮助

try
{
    someNormalFunction();
    cSmartPtr<BYTE> pBuf = malloc(1000);
    someExceptionThrowingFunction();    
}
catch(...)
{
    // Do logging and other necessary actions
    // but no cleaning required for <pBuf>
}
试试看
{
someNormalFunction();
cSmartPtr pBuf=malloc(1000);
someExceptionThrowingFunction();
}
捕获(…)
{
//执行日志记录和其他必要的操作
//但不需要清洁
}

例外的问题是——即使你用RAiI重新设计——仍然很容易使代码变得不同步:

void SomeClass::SomeMethod()
{
  this->stateA++;
  SomeOtherMethod();
  this->stateB++;
}
现在,这个示例看起来可能是人为的,但是如果您用stateA++和stateB++替换以某种方式更改类状态的操作,则该类的预期结果是状态保持同步。使用异常时,RAII可能会解决一些与状态相关的问题,但它所做的只是提供一种虚假的安全感-如果SomeOtherMethod()引发异常,则需要分析所有周围的代码,以确保满足post条件(stateA.delta==stateB.delta)。

此代码:

func1();
char *x=malloc(1000);
func2();

不是C++代码。这就是人们所说的带有类的C。它是一种看起来像C++的程序风格,但与C++在现实生活中的使用方式不匹配。原因是,;良好的异常安全C++代码实际上不需要在代码中直接使用指针(指针),因为指针总是包含在一个专门设计用来管理异常安全庄园(通常是智能指针或容器)中的生命周期的类中。 <>该代码的C++等价关系是:

func1();
std::vector<char> x(1000);
func2();
func1();
std::向量x(1000);
func2();

> p>硬件故障可能不会导致C++异常。在某些系统中,硬件异常是完全不同于C++异常的机制。在其他方面,C++异常是建立在硬件异常机制之上的。所以这不是一般的设计问题

如果您希望能够进行恢复,则需要进行事务性更改—每个状态更改都需要运行到完成或完全退出。RAII是其中的一部分。正如Chris Becke在另一个答案中指出的那样,国家不仅仅是获取资源

有一种复制-修改-交换习惯用法在事务中被大量使用,但如果您试图调整工作代码以处理这种百万分之一的情况,那么这种习惯用法可能会过于繁重


如果您确实需要健壮性,那么将代码隔离到流程中。如果硬件故障导致进程死亡,您可以让看门狗重新启动它。操作系统将回收丢失的资源。您的代码只需要担心事务性的持久状态,比如保存到文件中的内容。

关于这个示例,这正是解决问题的方式。对于类似的情况(一个24/7运行的守护进程),我的“解决方案”就是让一个cron作业每2分钟检查一次,以确保守护进程仍在运行,并在必要时重新启动。当抛出一个罕见的异常时,让守护进程死掉并在几分钟后重新启动通常不是什么大问题。如果您认为“硬件故障可能导致此代码中断”,那么是什么让您认为有可能编写代码来修复它?您的恢复代码也会受到影响。请阅读以下内容:为了理解RAIIyes解决的finally是一个糟糕的语言设计,我有源代码。我的问题不是如何修复这个示例,而是我可能会遇到哪些其他问题。如果我选择使用这个包装器并进行重构,您的回答会很有帮助。据我所知,您正在系统中实现一个软件“看门狗”。虽然你可以从大多数异常中恢复过来,但是有些情况下你不能假装事情从来没有发生过,并继续运行,也就是说堆栈损坏:)你应该总是使用RAII来管理C++中的资源。堆栈分配对象中的保护资源,其析构函数执行清理。然后您几乎可以删除所有的try/catch子句。在RAII实现之后,您将需要特定的错误控制(try/catch)。RAII只提供资源的故障安全处理。它不能防止操作/进程崩溃。如果您使用资源的宽泛定义,RAII技术可以轻松解决此问题。
func1();
std::vector<char> x(1000);
func2();