【问题标题】:custom exception class, using a base class, with multiple arguments自定义异常类,使用基类,带有多个参数
【发布时间】:2022-01-22 14:39:56
【问题描述】:

所以我正在为我正在编写的程序设置自定义异常类。我正在创建一个包罗万象的基类,我将主要将其用作通用异常。这个基类将被其他几个自定义异常继承。这是基类和附加异常类之一,将有 10+ 个子类从父类继承。

#include <exception>
#include <string>
#include <string.h>

class AgentException : public std::exception
{

protected:
  char *msg;

public:
  AgentException() : msg("AgentException"){};
  AgentException(char *m) : msg(m){};
  AgentException(char *m, std::string d)
  {

    strcat(m, d.c_str()); //aware this fails. its a segmentation fault
  };
  ~AgentException() = default;

  const char *what() const throw()
  {

    return (const char *)msg;
  }
};

class ConnectionFailed : public AgentException
{

private:
  std::string eType = "ConnectionFailed";

public:
  ConnectionFailed() : AgentException("ConnectionFailed"){};
  ConnectionFailed(std::string d) : AgentException("ConnectionFailed: ", d){};
  ~ConnectionFailed() = default;
};

我知道上面的代码 what() 目前不会返回任何东西,因为没有分配成员变量。我忽略了它,因为我从 strcat() 调用中遇到了分段错误。

我为父类创建了多个构造函数,因为有时我希望传递默认值、单个值甚至两个参数。对于子类,它总是至少将类 ID 传递给父类,在某些情况下,我可能需要将字符串变量与类 id 一起传递。字符串变量 std::string 是必须的。这些是我被赋予使用的指令。

最初我在类中将所有消息变量设置为 std::string,但我最终遇到了与 what() 函数相关的问题。我不知道如何将std::string 转换为const char*。在做了一些研究之后,我发现在异常类中使用字符串是一个坏主意,因为什么会捕获可能发生在其中的任何异常

所以我将所有内容都转换回const char*,但现在我似乎无法从what() 获得返回。这些问题都源于我无法弄清楚不同类型的串联。

通过对 AgentException 类的这种更改,我可以得到一些可以正常工作的东西。

protected:
  char msg[100];

public:
  // AgentException() : msg("AgentException"){};
  // AgentException(char *m) : msg(m){};
  AgentException(const char *m, std::string d)
  {

    strcpy(msg, m);
    strcat(msg, d.c_str());
    
  };

我可以让这种改变整体发挥作用,但感觉这不是正确的做法。有人可以告诉我他们将对此设置进行的更改吗?

我目前正在通过抛出 AgentException 或 ConnectionFailed 异常并使用 Base AgentException 捕获来进行测试。我一直在旋转,看看是否有任何不同的反应。

try
  {
    throw ConnectionFailed("test");
  }
  catch (const AgentException &e)
  {

    std::cout << "----------------" << std::endl;
    std::cerr << e.what() << '\n';
    std::cout << "_________________" << std::endl;
  }

【问题讨论】:

  • AgentException(char *m, std::string d) 不会初始化从what 返回的成员,从而导致第一个替代方案中的未定义行为。此外,您是否正在编写 C++11 之前的代码?否则what 的签名应该是const char* what() const noexcept;,您可以添加override 让编译器检查签名是否与基类中的签名匹配。
  • @fabian 我在第一部分代码中指出它当前正在引发异常。我没有将它分配给成员,因为 strcat 不起作用。我可以通过在代码的第二部分进行更改来使其工作。
  • 您是否有机会按价值捕获异常?您是否有理由不从 std::runtime_error 派生并让它处理存储 what 消息?
  • @AlanBirtles 所以我更新了我的帖子,向您展示我用来测试异常的代码部分。关于不是从 std::runtime_error 派生的,我没有答案。我会再读一读。它是否使用 what 消息管理多个参数?
  • 它从 what 返回,无论你在构造函数中传递什么,你仍然需要连接字符串来构建该消息,但你可以使用 std::string 来做到这一点

标签: c++ exception concatenation stdstring const-char


【解决方案1】:

正如 Silvio 在另一条评论中提到的,如果缓冲区不能包含结果字符串,则不能使用 strcat。在这种情况下,您还会遇到问题,即您将 const char * 传递给 strcat,这是一个否否(文字字符串是 const)。

我建议您将msg 变量更改为std::string。然后它将能够增长以容纳连接的消息,并为您管理内存。

class AgentException : public std::exception
{

protected:
  std::string msg;

public:
  AgentException() : msg("AgentException") {};
  AgentException(const char *m) : msg(m){};
  AgentException(const char *m, const std::string& d)
  {
      msg = m + d;
  };
  ~AgentException() = default;

  const char *what() const throw()
  {
      return msg.c_str();
  }
};

class ConnectionFailed : public AgentException
{
public:
  ConnectionFailed() : AgentException("ConnectionFailed"){};
  ConnectionFailed(const std::string& d) : AgentException("ConnectionFailed: ", d){};
  ~ConnectionFailed() = default;
};

【讨论】:

  • 我的印象是在异常中使用 std::string 作为成员变量的一部分是一种不好的做法,因为没有办法捕获错误。
  • 我不会将 std::string 用于“内存不足”异常,因为字符串需要分配。但是没有分配就没有办法做你需要的事情,并且在它自己的异常中分配不一定是一个问题。
【解决方案2】:

来自cppreference

char *strcat( char *dest, const char *src );

...

如果目标数组对于 src 和 dest 的内容以及终止空字符都不够大,则行为未定义。

因此,如果您使用足够大的 char* 调用此构造函数以存储 original 字符串,然后尝试在其上附加更多内容,则会出现未定义的行为。不要在不知道大小的缓冲区上调用strcat。您的第二个解决方案,您预先分配 100 个字符(假设这对您的用例来说足够了),然后使用 that 缓冲区是正确的。一个完全通用的解决方案会调用strlen 两次并分配一个足够大的缓冲区以容纳两个字符串,但如果这只是一个简单的示例,则可能超出范围。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-20
    • 1970-01-01
    • 2010-10-02
    • 2021-08-24
    • 2012-03-26
    • 1970-01-01
    • 1970-01-01
    • 2012-07-23
    相关资源
    最近更新 更多