c++ - C ++在地图中放置()时防止析构函数调用
问题描述
我有一个管理资源(网络套接字)的类。
我编写了一个类ConnectionHandler
来处理从调用创建的网络套接字accept()
。
这个类在设计时考虑了 RAII,当accept()
被调用时,返回的套接字被放入 aConnectionHandler
中,当它超出范围时,析构函数关闭套接字。
我还ConnectionHandler
通过将它们保存在地图中来跟踪我所有的 open (将套接字地址(IP:Port)映射到与ConnectionHandler
该地址对应的那个)。
不过,我在将这些“放置”ConnectionHandler
到地图中时遇到了问题。
我已经做到了 aConnectionHandler
不能被复制(至少我相信我已经做到了),但是在调用 时std::map::emplace
,会调用ConnectionHandler
的析构函数(大概是为了删除沿线某处创建的临时对象)和插座已关闭。
如您所见,这产生了一个问题,因为现在套接字不能在程序的后面进一步使用。
有什么办法可以防止在将ConnectionHandler
' 的析构函数放入 中时调用它std::map
?
这是ConnectionHandler
: 头文件的代码:
class ConnectionHandler
{
private:
constexpr static long BUFFER_SIZE = 1 << 12; // 4K Buffer
SocketAddress peer; // This is kept around to be able to produce clear exception messages when something goes wrong
SocketFileDescriptor socket; // using SocketFileDescriptor = int;
public:
ConnectionHandler() noexcept = delete; // Default Constructor
explicit ConnectionHandler(SocketFileDescriptor socket, const SocketAddress& socketAddress) noexcept; // Value Constructor
ConnectionHandler (ConnectionHandler&& handler) noexcept; // Move Constructor
ConnectionHandler (const ConnectionHandler& handler) = delete; // Delete Copy Constructor
ConnectionHandler& operator= (ConnectionHandler&& handler) noexcept; // Move Assignment Operator
ConnectionHandler& operator= (const ConnectionHandler& handler) = delete; // Delete Copy Assignment Operator
~ConnectionHandler(); // Destructor
void close() noexcept; // Allow the owner to manually close the socket if necessary
void set_blocking (bool blocking) const; // Make the socket either blocking or non-blocking
friend std::ostream& operator<< (std::ostream& stream, const ConnectionHandler& handler); // Receive data from the socket
friend std::istream& operator>> (std::istream& stream, const ConnectionHandler& handler); // Send data to the socket
};
和实施:
ConnectionHandler::ConnectionHandler(SocketFileDescriptor socket, const SocketAddress& socketAddress) noexcept: peer(socketAddress), socket(socket)
{
}
ConnectionHandler::ConnectionHandler(ConnectionHandler&& handler) noexcept: peer(std::move(handler.peer)), socket(handler.socket)
{
}
ConnectionHandler& ConnectionHandler::operator=(ConnectionHandler&& handler) noexcept
{
this->peer = std::move(handler.peer);
this->socket = handler.socket;
return *this;
}
ConnectionHandler::~ConnectionHandler()
{
if (this->socket > 0) // Check if the socket has been closed manually
// Don't bother setting the socket to -1, the object is being destroyed anyway
{
std::cout << "Closing socket from destructor " << this->socket << std::endl;
::close(this->socket);
}
}
void ConnectionHandler::close() noexcept
{
std::cout << "Closing socket from close() " << this->socket << std::endl; // Close the socket manually and indicate it is closed by setting it's value to -1
::close(this->socket);
this->socket = -1;
}
[...]
这就是 SocketAddress 类的样子(不适用于 IPv6,我知道):
class SocketAddress
{
private:
std::array<std::uint8_t, 4> ip;
std::uint16_t port;
public:
friend void swap (SocketAddress& sa1, SocketAddress& sa2) noexcept;
SocketAddress() noexcept;
explicit SocketAddress(struct sockaddr_storage* sockaddrStorage);
SocketAddress (const SocketAddress& address) = default;
SocketAddress (SocketAddress&& address) noexcept = default;
SocketAddress& operator= (SocketAddress address);
friend bool operator< (const SocketAddress& lhs, const SocketAddress& rhs) noexcept;
friend std::string to_string(const SocketAddress& address) noexcept;
};
最后,这是创建 ConnectionHandler 并将其放置在地图中的代码:
void Server::listenLoop() // acceptLoop() would be a better name
{
struct sockaddr_storage remoteAddr;
while(!stop) // stop is a std::atomic<bool>
{
[...] // accept() connections in a loop
SocketAddress address = SocketAddress(&remoteAddr);
this->incomingSockets.emplace(std::make_pair(address, ConnectionHandler(childFileDesc, address)));
}
[...]
}
该函数在与主线程分开的线程上运行,线程保存在 Server 对象中,并加入 Server 对象的析构函数中。
解决方案
在您的移动构造函数/赋值运算符中,您需要使移动的对象无效。析构函数仍将在从对象移动时调用。如果他们的套接字不是 0,那么析构函数仍然会在 fd 上调用 close。
推荐阅读
- database - 在一个数据库中存储大量文档与在不同数据库之间拆分它们对性能有何影响?
- java - 使用 javax.ws.rs.client.WebTarget 设置连接超时
- javascript - 开玩笑地模拟本地存储
- microsoft-graph-api - 通过 Microsoft Graph API 检索 SharePoint Online 对象权限
- raku - 是否可以在运行时在其他上下文中创建新的词汇符号?
- spring-boot - 使用 RestTemplate 和 Kotlin 数据类调用 Rest Endpoint
- javascript - REST'put','post' 400 错误请求。Javascript,HTML,Java
- java - java - 如何使用java 8在另一个函数中调用一个泛型函数超时?
- c++ - 使用函数指针推迟函数执行
- php - 如何记录特定用户执行的查询