如何编写从 std::invalid_argument 派生的自定义异常 class?

How to write a custom exception class derived from std::invalid_argument?

我应该如何编写一个高效的异常 class 来显示可以通过在 运行-time 之前修复源代码错误来避免的错误?

这就是我选择std::invalid_argument的原因。

我的异常 class(显然不起作用):

class Foo_Exception : public std::invalid_argument
{
private:
    std::string Exception_Msg;
public:
    explicit Foo_Exception( const std::string& what_arg );
    virtual const char* what( ) const throw( );
};

explicit Foo_Exception::Foo_Exception( const std::string& what_arg ) // how should I write this function???
{
    Exception_Msg.reserve( 130000 );

    Exception_Msg = "A string literal to be appended, ";
    Exception_Msg += std::to_string( /* a constexpr int */ );
    Exception_Msg += /* a char from a const unordered_set<char> */;
}

const char* Foo_Exception::what( ) const throw( )
{
    return Exception_Msg.c_str( );
}

一个 if 块抛出 Foo_Exception:

void setCharacter( const char& c )
{
    if ( /* an invalid character (c) is passed to setCharacter */ )
    {
        try
        {
            const Foo_Exception foo_exc;
            throw foo_exc;
        }
        catch( const Foo_Exception& e )
        {
            std::cerr << e.what( );
            return;
        }
    }

    /*
        rest of the code
    */
}

int main( )
{
    setCharacter( '-' ); // dash is not acceptable!

    return 0;
}

如您所见,我需要将 Exception_Msg 与几个子字符串连接起来才能形成完整的消息。这些子字符串不仅是字符串文字,而且是来自静态 std::unordered_set<char>constexpr ints 和 chars。这就是我使用 std::string 的原因,因为它具有易于使用的 string::operator+=。不用说,我的目标是将堆分配减少到只有 1。

另一个非常重要的问题是我应该在哪里放置处理程序( try-catch )?在 main() 内包裹 setCharacter() 或将其保留在 setCharacter?

请制作一个与上述类似的良好且标准的自定义异常class。或者自己写。提前致谢。

而不是每次 new char[],为什么不使用 std::string 数据成员?

class Foo_Exception : public std::exception /* ? */
{
    std::string msg;

public:
    Foo_Exception() {
        msg.reserve( 130000 );

        msg = "A string literal to be appended, ";
        msg += "another string, ";
        msg += "yet another one";
    }
    const char * what() const override { return msg.data(); }
    /* other members ? */
}

或者您可以派生自 std 异常类型之一,该异常类型将字符串作为构造参数

class Foo_Exception : public std::runtime_error /* or logic_error */
{
public:
    Foo_Exception() : runtime_error("A string literal to be appended, " "another string, " "yet another one") {}
    /* other members ? */
}

但是,如果您只有字符串文字,您可以可以将它们拆分到多个源代码行,并且分配。

const char * what() const override { 
    return "A string literal to be appended, "
           "another string, "
           "yet another one";
}

我根据别人通过评论和回答收到的反馈解决了这个问题。所以我决定在这里留下我自己的 answer/solution 供未来的读者使用。

下面是我经过深思熟虑和研究后得出的结论。这个解决方案相当简单易读。

这里是异常class 接口:

Foo_Exception.h

#include <exception>


class Foo_Exception : public std::invalid_argument
{
public:
    explicit Foo_Exception( const std::string& what_arg );
};

这是它的实现

Foo_Exception.cpp

#include "Foo_Exception.h"


Foo_Exception::Foo_Exception( const std::string& what_arg )
: std::invalid_argument( what_arg )
{
}

抛出 Foo_Exception:

函数

Bar.cpp

#include "Bar.h"
#include "Foo_Exception.h"


void setCharacter( const char& c )
{
    if ( /* an invalid character (c) is passed to setCharacter */ )
    {
        std::string exceptionMsg;
        exceptionMsg.reserve( 130000 );

        exceptionMsg = "A string literal to be appended, ";
        exceptionMsg += std::to_string( /* a constexpr int */ );
        exceptionMsg += /* a char from a const unordered_set<char> */;

        throw Foo_Exception( exceptionMsg );
    }

    /*
        rest of the code
    */
}

如何处理异常:

main.cpp

#include <iostream>
#include "Bar.h"
#include "Foo_Exception.h"


int main( )
{
    try
    {
        setCharacter( '-' ); // The dash character is not valid! Throws Foo_Exception.
    }
    catch ( const Foo_Exception& e )
    {
        std::cerr << e.what( ) << '\n';
    }

    return 0;
}

变更摘要:

  1. 注意不需要 what() 函数,因为编译器会隐式生成它,因为 Foo_Exception 继承自 std::invalid_argument.

  2. 此外,创建 异常消息 的过程已从 Foo_Exception 的构造函数移至函数体 setCharacter 实际上抛出上述异常。这样,Foo_Exception 的构造函数不负责创建消息。相反,它是在抛出的函数体中创建的,然后传递给 Foo_Exception 的构造函数以初始化新的异常对象。

  3. 数据成员 std::string Exception_Msg 也已从 Foo_Exception 中删除,因为不再需要它。

  4. 最后,try-catch 块被移动到 main() 以便它现在环绕 setCharacter() 并捕获 Foo_Exception 它可能抛出的对象。

最后一句话: 非常感谢任何进一步改进我的答案的建议。 非常感谢所有反馈。