如何在不将字符串对象嵌入自定义异常类的情况下重新格式化错误消息?

时间:2011-12-21 07:55:39

标签: c++ exception exception-handling

我已阅读以下问题的答案 - >

c++ exception : throwing std::string

c++ Exception Class Design

我的要求是捕获异常并允许机制重新格式化错误消息,然后重新抛出异常。 所以我想编写我的异常类,它将提供一种在各种捕获点重新格式化错误消息的方法。为了使重新格式化变得容易,我在myexception_base类中嵌入了一个字符串对象。

我也提到了这篇文章 - > http://www.boost.org/community/error_handling.html明确表示不要在异常类中嵌入字符串对象。

该文章还引用了一句话:“Peter Dimov提出了一个很好的论据,即正确使用what()字符串作为错误消息格式化表的一个关键。”但它没有进一步阐述它。

我已经编写了下面给出的代码,我的问题是 -

1)如果我没有在我的异常类中嵌入一个字符串对象,那么将很难重新格式化哪些消息。请告诉我如果不在我的异常类中嵌入字符串对象,我将如何处理我的要求?

2)如果我继续使用我当前嵌入字符串对象的方法并提供格式化错误消息的方法。我处理某些情况的方式是否正确? (问题1和问题2,其中错误消息未被格式化)

问题1 - 错误的异常被传播到捕获点? 问题2 - 错误消息在某些情况下没有格式化? 下面的代码中的注释中提到了问题1和问题2。

请查看代码。我在代码中给出了很多注释,因此代码行增加了,但是如果删除注释,它的代码行就会少一些。

#include <string>
#include <exception>
#include <iostream>
using namespace std;

class myexception_base : public exception {

private:
    string error_msg;
public:
    myexception_base(const char* str) : error_msg(str) {    
        // std::string here here can throw an exception while error_msg 
        //initialisation through MIL. 
        // myexception_base object will not be constructed 
        // and wrong exception will be  propagated to catch point(Issue 1)
    }

    const char* what() const throw() {  

        try {
            return error_msg.c_str();   
        } catch (...) {}

        // c_str can throw. 
        // I can't remove throw specification from what()
        // because what() in base std::exception has throw() specification  
        // In case c_str() throws an exception program will terminate
        // So to stop abnormal termination of program c_str 
        // is wrapped in a try catch block to ignore std::string exception
        // program will not terminate but what message is not reformatted
        // in such scenario (Issue 2)
    }

    void append_error_msg(const char* str) {

        error_msg.append(str);      
        // append can throw
        // Again error_msg will not be formatted (Issue 2)
    }
    ~myexception_base() throw() {
    }
};

void f();
void f2();

void f() {
    throw myexception_base("Error Msg1");
}

void f2() {

    //Some intermediate Catch point will reformat e.what() msg
    //by appending an error msg2
    try {
        f();
    }
    catch (myexception_base& e) {

        e.append_error_msg(": Error Msg2");
        throw;
    }
}


int main () {
    try {
        f2();
    }
    catch(const exception& e) {
        // Finally the exception is caught in the upper most layer
        // and proper error_msg is given to user
        cout<<e.what()<<"\n";
    }
}

1 个答案:

答案 0 :(得分:2)

根据boost文档,嵌入式类在它们的生命周期中可能抛出并不是在异常处理程序机制中使用的优秀候选者,因此我建议您只使用旧的C语言处理字符串。它需要一些额外的努力才能启动并运行,但是使用它们可以消除一个错误来源。

所以,基本上有两种方法:

  1. 在myexception_base中,您将error_msg声明为N个字符的数组。在append_error_msg中,您仔细检查NEW字符串的长度(在追加之后)是否不大于N,如果不是,则使用strcat或其他C字符串处理方法之一。这种方法可能很好,因为它不允许通过异常处理机制泄漏内存,但是你只能有一个有限长度的错误消息。

  2. 在myexception_base中,您将error_msg声明为char *(char指针)。在构造函数中,您为calloc内存(不要使用new,因为new可能抛出)和append_error_message中realloc指针是新大小(即旧大小+字符串)要为0)附加.size + 1,然后memcpy到所请求的位置(即在新分配的字符串中旧字符串末尾的位置)。当然,不要忘记检查他们返回的所有分配/重新分配有效值,否则您可能会得到有趣的行为。最后,但并非最不重要的是,为了避免经典的内存泄漏,释放析构函数中char *的内存。

  3. 祝你好运!