【问题标题】:Correctly catching the CDatabase::Close exception正确捕获 CDatabase::Close 异常
【发布时间】:2021-12-09 10:28:34
【问题描述】:

我想我会深入研究一下捕获异常。

根据这个问题 (C++ catching all exceptions) 的答案之一是:

[catch(...)] 将捕获所有 C++ 异常,但它应该被视为糟糕的设计。


目前我已经使用了这种方法:

CPTSDatabase::~CPTSDatabase()
{
    try
    {
        CloseDatabase();
    }
    catch(...)
    {
    }
}

void CPTSDatabase::CloseDatabase()
{
    if (m_dbDatabase.IsOpen())
        m_dbDatabase.Close();
}

我认为这是正确的方法,因为当我追踪到 CDatabase::Close() 时,它会执行类似的操作:

// Disconnect connection
void CDatabase::Close()
{
    ASSERT_VALID(this);

    // Close any open recordsets
    AfxLockGlobals(CRIT_ODBC);
    TRY
    {
        while (!m_listRecordsets.IsEmpty())
        {
            CRecordset* pSet = (CRecordset*)m_listRecordsets.GetHead();
            pSet->Close();  // will implicitly remove from list
            pSet->m_pDatabase = NULL;
        }
    }
    CATCH_ALL(e)
    {
        AfxUnlockGlobals(CRIT_ODBC);
        THROW_LAST();
    }
    END_CATCH_ALL
    AfxUnlockGlobals(CRIT_ODBC);

    if (m_hdbc != SQL_NULL_HDBC)
    {
        RETCODE nRetCode;
        AFX_SQL_SYNC(::SQLDisconnect(m_hdbc));
        AFX_SQL_SYNC(::SQLFreeConnect(m_hdbc));
        m_hdbc = SQL_NULL_HDBC;

        _AFX_DB_STATE* pDbState = _afxDbState;

        AfxLockGlobals(CRIT_ODBC);
        ASSERT(pDbState->m_nAllocatedConnections != 0);
        pDbState->m_nAllocatedConnections--;
        AfxUnlockGlobals(CRIT_ODBC);
    }
}

CDatabase::Close 文档甚至没有说明任何关于抛出异常的信息。


链接的答案确实说明:

可以使用c++11新的current_exception机制。

鉴于我们正在使用的CDatabase 类,我们是否可以使用这种方法尚不清楚。

【问题讨论】:

  • 在一个不相关的注释上,CDatabase::Close() 完全做了其他事情。它实现了在 Java 或 C# 等语言中常见的 try/finally 模式。它允许标记需要运行的代码区域,无论是否引发异常。释放锁肯定是你一直希望发生的事情,这就是整个 try-catch-unlock-rethrow 舞蹈正在做的事情。如果今天重新实现 MFC,它只需使用 scoped_lock,所有 try-catch-rethrow 噪音都会神奇地消失。

标签: c++ exception visual-c++ mfc cdatabase


【解决方案1】:

由于CDatabase::Close() 使用THROW_LAST 来抛出CDBException,你必须使用catch (CDBException* e)。即使您不处理它,您仍然必须Delete 错误。当直接调用CDatabase 方法时,您不妨这样做:

void CPTSDatabase::CloseDatabase()
{
    try
    {
        if (m_dbDatabase.IsOpen())
            m_dbDatabase.Close();
    }
    catch (CDBException* e) 
    { 
        //TRACE(L"DB error: " + e->m_strError); 
        e->Delete(); 
    }
}

或者使用

CPTSDatabase::~CPTSDatabase()
{
    try { CloseDatabase(); }
    catch (CDBException* e) { e->Delete();  }
    catch(...) {}
}

因为在这段代码中并不清楚异常来自哪里。 catch(...) {} 将处理其他异常。一般不推荐catch(...) {},因为它没有提供有用的信息,它只是说“出了点问题......”

仅当您在自己的代码中添加throw 或使用std 函数时才使用标准库异常。示例:

try { std::stoi("wrong argument"); }
catch (const std::exception& e) { TRACE("%s\n", e.what()); }

try { throw 123; }
catch (int i) { TRACE("%d\n", i); }

【讨论】:

  • 我必须消化这个!我以为我必须处理它,因为我看到了 THROW_LAST。
  • 抱歉,我没有看到最后的投掷
猜你喜欢
  • 1970-01-01
  • 2018-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
相关资源
最近更新 更多