什么是清理代码的正确方法?

当你有几行代码启动其他对象时,是否有比下面显示的清理对象更清洁的方法? 我在一个函数中启动了多个对象,并检查它们是否失败 – 但是我有一堆冗余代码,我不得不继续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进行错误管理?