如何优雅地处理异常抛出的构造函数?

时间:2011-10-28 08:43:09

标签: c++ memory

我有时想知道如何处理可以抛出构造函数的对象的构造。我不知道你是怎么做到的。

请考虑以下代码段。我有一个名为TCPMessage的类,它代表我的“服务器”通过TCP接收的消息。如果收到的消息无效(即在TCPMessage的构造函数中计算的CRC32没有检出),则TCPMessage的构造函数抛出。

以下是我的表现方式。你知道更好的方法吗?我问,因为它看起来并不太优雅。

void TCPConnection::handleRead(
  const boost::system::error_code& error,
  char* read_buffer
)
{
  if (!error) {
    TCPMessage* message = NULL; // being verbose here
    try {
      message = new TCPMessage(read_buffer);
    } catch (const char* e) {
      std::cerr << "Instatiating TCPMessage: " << e << std::endl;
    } catch (...) {
      std::cerr << "Instatiating TCPMessage: unknown exception." << std::endl;
    }

    if (message) {
      // if created succesfully
      // process the message and delete it
      SeekurJrRC::Core::Driver& driver = boost::asio::use_service<SeekurJrRC::Core::Driver>(_io_service);
      driver.processMessage(*message);
      delete message;
    }
  }
  delete [] read_buffer;
}

哦,是的,我知道知道比使用char* read_buffer并在另一个函数中删除它更好。 shared_ptr就是这样,我知道。

5 个答案:

答案 0 :(得分:5)

你的问题不是例外,它是原始指针和缺乏RAII。

稍微清理你的代码:

void TCPConnection::handleRead(
  const boost::system::error_code& error,
  char* read_buffer
)
{
  if (!error) {
    try {
      TCPMessage message(read_buffer);
      SeekurJrRC::Core::Driver& driver = boost::asio::use_service<SeekurJrRC::Core::Driver>(_io_service);
      driver.processMessage(message);

    } catch (const char* e) {
      std::cerr << "Instatiating TCPMessage: " << e << std::endl;
    } catch (...) {
      std::cerr << "Instatiating TCPMessage: unknown exception." << std::endl;
    }
  }
}

new调用应该包含在RAII对象中,而不是在用户代码中自由悬挂。 delete调用永远不会显式,而是由RAII对象中定义的析构函数处理。

然后,如果抛出异常,您的对象将自动被破坏并自行清理,并且您不需要过度复杂的“尝试/捕获/检查成功”舞蹈。如果没有抛出异常,则继续正常。如果它被抛出,则保留try块,并自动销毁对象。

请注意,此处您实际上不需要 try / catch块。您使用catch的唯一方法是打印错误消息。程序流程没有必要或防止资源泄漏。通常你会处理错误,你可以有意义地这样做。据推测,在调用树上稍高一些,你知道如何处理失败的读取。在这个级别,让异常转义以指示发生错误更有意义。

void TCPConnection::handleRead(
  const boost::system::error_code& error,
  char* read_buffer
)
{
  if (!error) {
    TCPMessage message(read_buffer);
    SeekurJrRC::Core::Driver& driver = boost::asio::use_service<SeekurJrRC::Core::Driver>(_io_service);
    driver.processMessage(message);
  }
}

答案 1 :(得分:0)

某处你必须处理异常。如果您不希望每次使代码混乱,那么您可以使用构造函数的特殊语法仅在一个地方处理异常。 e.g。

new TCPMessage(read_buffer)

答案 2 :(得分:0)

将处理包含在try-catch-block中有什么问题?

void TCPConnection::handleRead(
  const boost::system::error_code& error,
  char* read_buffer
)
{
  if (!error) {
     try {
         std::auto_ptr<TCPMessage> message(new TCPMessage(read_buffer));

         // if created succesfully
         // process the message and delete it
         SeekurJrRC::Core::Driver& driver = boost::asio::use_service<SeekurJrRC::Core::Driver>(_io_service);
         driver.processMessage(*message);
     } catch (const char* e) {
         std::cerr << "Instatiating TCPMessage: " << e << std::endl;
     } catch (...) {
         std::cerr << "Instatiating TCPMessage: unknown exception." << std::endl;
     }
  }
  delete [] read_buffer;
}

当然,当代码的另一部分可以抛出时,你应该使用合适的智能指针来实现异常安全。在此函数中不处理所有异常也可能是好的。

答案 3 :(得分:-1)

通常的方法是在可以解决问题的地方处理异常。

例如,如果“连接被丢弃”异常被抛出,您可以在某个地方处理它,您可以询问用户该怎么做:重新连接,或者关闭应用程序。

您不一定需要在该函数中处理异常。我看到你只打印错误信息。如果这是唯一要做的事情,那就没关系。正如你所说,使用智能指针会更好。像这样:

void TCPConnection::handleRead(
  const boost::system::error_code& error,
  char* read_buffer
)
{
  if (!error) {
    try{
    std::auto_ptr< TCPMessage > message( new TCPMessage(read_buffer) );
    SeekurJrRC::Core::Driver& driver = boost::asio::use_service<SeekurJrRC::Core::Driver(_io_service);
    driver.processMessage(message.release());

    } catch (const SomeException e) {
      std::cerr << "Instatiating TCPMessage: " << e << std::endl;
    } catch (...) {
      std::cerr << "Instatiating TCPMessage: unknown exception." << std::endl;
    }

  }
  delete [] read_buffer;
}

请注意,最好是捕捉一些自定义异常类型,而不是const char*

答案 4 :(得分:-1)

构造函数应该只处理与资源分配相关的作业。如果我们在使用它之前需要进行复杂的初始化,我们需要一个专用的init成员函数。这就是ATL的作用。这样我们就可以避免在构造函数中抛出异常。

智能指针无法解决问题,当构造函数中抛出异常时,我们可能会发生内存泄漏。

    std::autor_ptr<Widget> pWidget(new Widget()); 

以上陈述至少会做三件事:

   1. call new operator to allocate memory
   2. construnct an object by calling its constructor 
   3. constuct the smart poniter. 

如果在第二步中抛出异常,则不构造智能指针,它永远不会被破坏。结果,我们泄漏了第一步中分配的内存。