当你有几行代码启动其他对象时,是否有比下面显示的清理对象更清洁的方法? 我在一个函数中启动了多个对象,并检查它们是否失败 – 但是我有一堆冗余代码,我不得不继续input。 下面显示了正确的方法吗? 还是有更清洁的方法? 我知道do(false)方法和goto方法,但它们不干净,感觉混乱。
if( bind(s, (sockaddr *)&saddr, sizeof(sockaddr)) == SOCKET_ERROR ) { printf("bind() failed.\n"); closesocket(s); CloseHandle(g_hIOCompletionPort); CloseHandle(g_hShutdownEvent); WSACleanup(); } if( listen(s, 60) == SOCKET_ERROR ) { printf("listen() failed.\n"); closesocket(s); CloseHandle(g_hIOCompletionPort); CloseHandle(g_hShutdownEvent); WSACleanup(); } g_hAcceptEvent = WSACreateEvent(); if( g_hAcceptEvent == WSA_INVALID_EVENT ) { printf("WSACreateEvent() failed.\n"); closesocket(s); CloseHandle(g_hIOCompletionPort); CloseHandle(g_hShutdownEvent); WSACleanup(); }
大多数情况下,RAII将被用在C ++中,其中代码是用具有构造函数和析构函数的对象来组织的,并且在析构函数中执行清理。 因此,破坏对象就足够了,这限制了复制代码的数量。
class server { SOCKET s; HANDLE iocp; HANDLE shutdown; std::string err_str; public: ~server () { if (!err_str.epmty()) std::cerr << err_str << '\n'; closesocket(s); CloseHandle(iocp); CloseHandle(shutdown); WSACleanup(); } //... };
除了RAII之外,C ++还提供异常处理,也可以在你的情况下工作。 try
块会有套接字代码。 当套接字代码发生错误时会抛出异常,并且catch
块可以负责清理。
try { if (bind(...) == SOCKET_ERROR) { throw ...something...; } if (listen(...) == SOCKET_ERROR) { throw ...something...; } ... } catch (...something...) { ...cleanup code; }
在C中,没有RAII等价物。 也没有例外。 然而,异常处理可以用setjmp()
和longjmp()
来模拟,就像cexcept
。 尽管对RAII没有直接的支持,但是没有任何东西阻止您将C代码组织到具有相关清理功能的对象中。
struct server { SOCKET s; HANDLE iocp; HANDLE shutdown; const char *err_str; }; void destroy_server (server *server) { /* ... */ }
如果你有很多代码遵循一个序列,但是其中的任何一个可能都需要进行清理,那么你可以在状态机中组织代码,如时尚。
enum { STATE_INIT, STATE_SUCCESS, STATE_ERROR, STATE_STOP } state = STATE_INIT; while (state != STATE_STOP) { switch (state) { case STATE_INIT: state = do_server_init(); break; case STATE_SUCCESS: state = do_server(); break; case STATE_ERROR: state = do_server_cleanup(); break; case STATE_STOP: break; default: fprintf(stderr, "unexpected state: %d\n", state); state = STATE_ERROR; } }
如果您使用C ++,那么RAII(资源获取初始化)是首选方法。 基本上你可以获得或附加一个资源(通常在施工期间),并在销毁期间释放资源。
例如,在你的代码中,你会得到一些Handle
类,它保存在g_hIOCompletionPort
句柄上,并在其析构函数中调用CloseHandle
。
在C ++中, RAII是这种情况下非常常见的模式。
对于简单的C:如果我写goto我会被杀害,所以我不知道。 但是看看这里的例子: 在C中有效的使用goto进行错误管理?