我有时想知道如何处理可以抛出构造函数的对象的构造。我不知道你是怎么做到的。
请考虑以下代码段。我有一个名为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
就是这样,我知道。
答案 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.
如果在第二步中抛出异常,则不构造智能指针,它永远不会被破坏。结果,我们泄漏了第一步中分配的内存。