【问题标题】:Throwing exception from destructor of RAII class从 RAII 类的析构函数中抛出异常
【发布时间】:2016-01-26 14:16:22
【问题描述】:

我创建了一个类来封装数据库事务,以确保在引发异常时回滚或提交它。问题是创建和关闭事务都可能失败。由于事务在析构函数中被关闭,我怎样才能在不抛出异常的情况下处理失败?显然,如果TransactionLock 对象由于堆栈某处抛出异常而被销毁,这将导致程序终止。

// RAII class for database transaction
class TransactionLock {
public:
    TransactionLock(QSqlDatabase& db) :
        m_db(db),       
        m_query(db),
        m_committed(false)
    {
        bool ok = m_query.exec("BEGIN IMMEDIATE TRANSACTION");

        if (!ok)
        {
            throw IOException(m_query.lastError());
        }
    }

    ~TransactionLock() 
    {
        bool ok = m_committed ? m_db.commit() : m_db.rollback();

        // if (!ok) throw?
    }

    void commitTransaction()
    { 
        m_committed = true;
    }       

private:
    QSqlDatabase& m_db; 
    QSqlQuery m_query;
    bool m_committed;   
};

【问题讨论】:

    标签: c++ database exception raii


    【解决方案1】:

    你永远不应该抛出析构函数。不仅因为异常堆栈展开可以调用析构函数,还因为它在逻辑上毫无意义。

    你无法阻止对象被破坏,所以异常根本不会给你任何东西。在您的特定情况下,您需要重新设计您的课程。标准方法通常是在析构函数中进行自动回滚。提交事务是带有错误代码的显式操作。

    【讨论】:

    • 但是你可以从析构函数中抛出。标准中没有任何内容禁止它。我不建议这样做,但可以做到。
    • @NathanOliver,这让我想起了一个老笑话。 “他们说我踢足球时不能拿着球跑,但我只是尝试过——并且可以毫无问题地做到这一点!”。你可以,但不应该。
    • @NathanOliver - 我已经编辑了开场白,所以它不是那么夸张。情绪是正确的。
    • 好的,这有点改进,但​​.rollback() 仍然可能失败。我应该忽略它吗?
    • @jaho,您使用的是哪个数据库?我曾与多个合作过,但我从未听说过有一个可能会导致回滚失败。
    猜你喜欢
    • 2014-07-06
    • 2022-01-16
    • 2015-08-26
    • 2016-12-17
    • 2013-04-03
    • 2012-02-25
    • 2016-07-21
    相关资源
    最近更新 更多