使用 try-catch 进行日志记录、抛出非运行时错误的用户定义类型是一个好习惯吗?

问题描述 投票:0回答:1

我正在编写代码,当用户单击按钮时,读取文件、解析文件并将数据存储到数据库中。我已将每个操作分成多个类中的函数,但其中许多函数都会进行数据库查询。例如:

// in ui class cpp
void btnClicked(){
    const QStringList files = ...; // select files to load
    FileHandler handler;
    int succ = handler.loadFromFiles(files);
    if(succ != files.size()) // when not all files are loaded
        logging(...);
}

// FileHandler.cpp
class FileHandler {
public:
    int loadFromFiles(...) {
        read(...);  // N queries
        myParser->parse(...);  // M queries
        myDBHandler->insert(...);  // O queries
    }
private:
    Parser* myParser;
    DBHandler* myDBHandler;
};

为了确保可靠性,我想验证每个查询是否成功执行,并在出现问题时记录任何错误。我可以做这样的事情:

// during each query
QSqlQuery query("SELECT * FROM myTable");
if(!query.isActive()) log(query.lastError());

但是,由于涉及多个类,这将导致跨不同类的冗余错误处理代码,并且由于已经记录成功加载的文件数量而需要写入额外的不必要的日志。

因此,我正在考虑在发生故障时抛出 QSqlError 并在用户输入事件函数中捕获它以记录错误。结构如下所示:

// ui class cpp
void btnClicked(){
    const QStringList files = ...; // select files to load
    FileHandler handler;
    int succ = 0;
    try { 
        succ = handler.loadFromFiles(files);
    } catch (const QSqlError& e) {
        logging(e.text());
        return;
    }
    if(succ != files.size()) // when not all files are loaded
        logging(...);
}

// during each query
QSqlQuery query("SELECT * FROM myTable");
if(!query.isActive()) throw query.lastError();

但是,我对使用 try-catch 块不太熟悉,所以我不确定这种结构是否合适。我已确保所有资源均已正确释放,并且主事件循环保持完整,并且我了解 SQL 查询失败并不是严重的运行时错误。然而,许多程序和文档建议抛出从 std::exception 派生的对象并在 catch 块中终止程序。

这种用法被认为是一个好的做法吗?

c++ qt try-catch
1个回答
0
投票

无论如何,如果您有需要沿着调用堆栈传播错误的代码,那么抛出异常是一个很好的解决方案。

夸张的例子,而不是有:

int dbAccessFail() {
    std::cerr << "failed\n";
    return -1;
}

int foo() {
    // calling db
    int errCode = dbAccessFail();
    if (errCode != 0) { /* error handling */ return errCode; }
    // more ...
    return 0;
}

int bar() {
    // something ...
    int errCode = foo();
    if (errCode != 0) { /* error handling */ return errCode; }
    // more ...
    return 0;
}

这显然不能很好地扩展,你应该:

void dbAccessFail() {
    std::cerr << "failed\n";
    throw std::runtime_error("failed");
}

void foo() {
    // calling db
    dbAccessFail();
    // more ...
}

void bar() {
    // something ...
    foo();
    // more ...
}

最后

bar
的调用者可以决定捕获异常并处理它,例如:

    try {
        bar();
    } catch(const std::exception& e) {
        std::cerr << "exception: " << e.what() << '\n';
        // some cleanup, retry, error dialog, or whatever
    }

然后在您的具体情况下,您必须小心,不要让异常逃脱 Qt 事件循环,因此最好在每个调用可能抛出方法的插槽中使用

try-catch
块。否则它被认为是未定义行为,而不仅仅是
std::terminate

© www.soinside.com 2019 - 2024. All rights reserved.