gpt4 book ai didi

c++ - 在 C++ 中从构造函数显式调用析构函数是不好的做法吗?

转载 作者:行者123 更新时间:2023-12-03 06:52:39 27 4
gpt4 key购买 nike

我通常不会显式调用析构函数。但我正在设计 TCP 服务器类,它看起来像这样:

class Server {
public:
Server() {
try {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
throw std::runtime_error("WSAStartup function failed.");
...

if ((m_scListener = socket(pAddr->ai_family, pAddr->ai_socktype, pAddr->ai_protocol)) == INVALID_SOCKET)
throw std::runtime_error("'socket' function failed.");
...
}
catch (std::exception& ex) {
this->~Server();
throw;
}
}

~Server() {
if (m_scListener != INVALID_SOCKET) {
closesocket(m_scListener);
m_scListener = INVALID_SOCKET;
}
WSACleanup();
}
private:
SOCKET m_scListener = INVALID_SOCKET;
}
上面的代码是否被认为是不好的做法或设计?推荐的设计方法是什么?我是这样写的,因为构造函数不能返回 NULL。我应该将构造函数设为私有(private),并编写创建 Server 类实例的静态方法吗?
===== U P D A T E =====
好的,总结答案,我得出了这个结论:
  • 显式调用析构函数通常不是一个好主意,即使它按预期工作,这也是不寻常的,其他将处理您的代码的 C++ 程序员可能会对这种方法感到困惑。所以最好避免显式调用析构函数。
  • 将我原来的 RAII 类分解为微型 RAII 类看起来是一个很好的解决方案。但我担心我的真实代码中有太多 API 调用需要清理(closesocket、CloseHandle、DeleteCriticalSection 等)。其中一些只被调用一次并且从未被重用,并且将它们全部移动到单独的 RAII 类中对我来说似乎太狂热了。这也会增加我的代码。
  • 在我看来,最有帮助的答案来自 MM :

  • A better solution would be to keep the initialization code in theconstructor, and call the cleanup function before throwing out.


    按照 M.M 的建议,我以这种方式重写了我的代码:
    class Server {
    public:
    Server() {
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData))
    ThrowError("WSAStartup function failed.", true);
    ...

    if ((m_scListener = socket(pAddr->ai_family, pAddr->ai_socktype, pAddr->ai_protocol)) == INVALID_SOCKET)
    ThrowError("'socket' function failed.", true);
    ...
    }

    ~Server() { CleanUp(); }

    private:
    SOCKET m_scListener = INVALID_SOCKET;

    void ThrowError(const char* error, bool cleanUp) {
    if (cleanUp)
    CleanUp();
    throw std::runtime_error(error);
    }

    void CleanUp() {
    if (m_scListener != INVALID_SOCKET) {
    closesocket(m_scListener);
    m_scListener = INVALID_SOCKET;
    }
    WSACleanup();
    }
    };
    我相信这个设计遵循 RAII 模式,但只有一个类而不是 3-4 个微 RAII 类。

    最佳答案

    Is explicitly calling destructors from constructors bad practice in C++?


    是的。如果调用尚未构造的对象的析构函数,则程序的行为是未定义的。
    有未定义的行为是一件坏事。应尽可能避免。

    What's recommended way of designing this?


    遵循单一职责原则 (SRP) 和资源获取即初始化 (RAII) 模式。
    特别是,您的 Server有太多的责任。您应该创建一个单独的类来管理套接字。在该类的构造函数中,调用 scoket在析构函数中,调用该类,调用 closesocket .保持包含的套接字始终有效(可关闭)或 INVALID_SOCKET 的类不变量并且如果有效并且永远不会泄漏,则始终是唯一的(即,在没有先关闭的情况下永远不会覆盖该值)。这是 RAII 模式。
    为 wsa 数据创建一个类似的包装器。
    Server 内, 存储这些包装器类型的成员。 Server然后不需要自定义析构函数或其他特殊成员函数,因为它们由管理自己的成员处理。

    关于c++ - 在 C++ 中从构造函数显式调用析构函数是不好的做法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64708587/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com