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

标签 c++ c windows

当您有几行代码启动其他对象时,是否有比下面显示的更干净的方法来清理对象?我在一个函数中启动了多个对象,并检查它们是否失败——但我有一堆冗余代码,我必须继续输入。下面显示的是执行此操作的正确方法吗?或者有更清洁的方法吗?我知道 do { } while(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 block 将包含套接字代码。当套接字代码可能因错误而抛出异常时,catch block 可以负责清理。

try {
    if (bind(...) == SOCKET_ERROR) {
        throw ...something...;
    }
    if (listen(...) == SOCKET_ERROR) {
        throw ...something...;
    }
    ...
}
catch (...something...) {
    ...cleanup code;
}

在 C 中,没有等效的 RAII。也不异常(exception)。但是,可以使用 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++ - 清理代码的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22561381/

相关文章:

c++ - 当我编译我的 C++ 代码时,avast 认为它是病毒

c++ - 测试元素是否使用 C++17 折叠表达式排序

c++ - #define 在 Boost Jamfiles 中

c++ - 如何绑定(bind) C++ 公共(public)变量以在 lua 脚本中访问

c# - 未找到 BackgroundWorker

c++ - 从 boost 1.38 升级到 1.39 导致我对 boost::algorithm::split 的调用无法编译

c - 在 C 中追加数字

c - printf中的 "%.6d"是什么意思

windows - swf转exe,真实体验

windows - Windows 中的进程间同步屏障