如何优雅地处理异常抛出构造函数
How to elegantly handle exception-throwing constructors?
我有时想知道如何处理可以放入构造函数的对象的构造。我想知道你是怎么做的。
请考虑以下片段。我有一个名为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
,我知道。
您的问题不是例外,而是原始指针和缺乏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);
}
}
您必须在某个地方处理异常。如果您不想每次执行new TCPMessage(read_buffer)
时都将代码弄乱,那么您可以为构造函数使用特殊语法,只在一个位置处理异常。例如
class TCPMessage {
...
public:
TCPMessage (const char *read_buffer)
try {
...
}
catch(const char *p){ ... }
catch(...) { ... }
...
};
将处理封装在try-catch块中有什么问题?
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;
}
当然,当代码的另一部分可以抛出时,您应该使用合适的智能指针来确保异常安全。最好不要在这个函数中正确处理所有异常。
通常的方法是在可以解决异常的地方处理异常。
例如,如果抛出"connection was droped"异常,您可以在某个地方处理它,询问用户该怎么办:重新连接,或者关闭应用程序。
您不一定需要在该函数中处理异常。我看到你只是在打印错误信息。如果这是唯一可以做的事情,那么没关系。正如你所说,最好使用智能指针。类似这样的东西:
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*
。
构造函数应该只处理与资源分配相关的作业。如果在使用之前需要进行复杂的初始化,我们需要一个专用的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.
如果在第二步中抛出异常,则不会构造智能指针,也不会对其进行销毁。因此,我们泄漏了在第一步中分配的内存。
- 从构造函数抛出异常时如何克服内存泄漏
- 如何编写带有异常的构造函数
- 从 C++ 中异常的构造函数引发异常
- 我正在尝试创建一个使用 c++ 中的参数包构造函数的异常类
- 稍后在构造函数中重新启动异常指令删除此指令
- 自定义异常中的用户定义的空构造函数,具有多个继承和抽象基类
- 构造函数中引发的异常
- 如何捕获源自静态分配对象的构造函数的异常?
- 从构造函数内存泄漏引发异常
- 智能指针和构造函数异常
- 构造函数C++异常说明符
- boost::archive::text_iarchive构造函数异常
- C++堆栈对象的构造函数异常处理
- 默认构造函数C++异常不会引发吗?
- 当函数中静态变量的构造函数异常终止时会发生什么
- 在c++中,如果基类构造函数异常,则构造函数和析构函数的顺序可以是这样
- 如何捕获构造函数异常
- C++构造函数异常处理
- 捕获构造函数异常的RAII方法
- Std::unique_ptr::reset和构造函数异常