如何设置具有RAII类的std容器?

时间:2013-07-25 17:02:44

标签: c++ raii

为了实现类型SOCKET的{​​{3}}习语,我创建了一个包装器。包装器在构造函数中调用connect,在析构函数中调用closesocketstd::map保存所有使用过的套接字。不幸地将新套接字插入容器中会调用临时的析构函数,实际上会关闭刚刚打开的套接字。是否有一种解决这个问题的常用方法?

以下是代码:

#include <iostream>
#include <stdexcept>
#include <map>
#include <winsock2.h>

struct Socket {
    SOCKET mSock;
    Socket() : mSock(INVALID_SOCKET) {}
    Socket(std::string ip, int port);
    ~Socket();
};

Socket::Socket(std::string ip, int port) {
    mSock = socket(AF_INET, SOCK_STREAM, 0);
    if (mSock == INVALID_SOCKET)
        throw std::runtime_error("socket()");
    SOCKADDR_IN addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
    if (connect(mSock, reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr))
        == SOCKET_ERROR)
        throw std::runtime_error("connect()");
    std::cout << mSock << " connected" << std::endl;
}

Socket::~Socket() {
    if (mSock != INVALID_SOCKET) {
        closesocket(mSock);
        std::cout << mSock << " closed" << std::endl;
    }
}

int main() {
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 0), &wsa);
    std::map<int, Socket> outbound;
    // calls constructur but also destructor
    outbound[0] = Socket("192.168.128.125", 4023);
    WSACleanup();
    return 0;
}

输出是:

1952 connected
1952 closed
1952 closed

2 个答案:

答案 0 :(得分:4)

如果将资源保护包装在智能指针中,则可以将资源保护存储在STL容器中。哪个智能指针完全取决于您的环境,如果您不确定,我建议您使用boost :: shared_ptr作为起点。

按照这种方式,您将符合资源保护语义(只有一个管理资源生命周期的实例)和STL容器(保留传递项目的等效副本)。

答案 1 :(得分:1)

正如其他人所提到的,你应该阻止复制你的Socket对象,这可以通过添加:

来完成
private:
  Socket(const Socket &){}
  Socket & operator=(const Socket &){return *this;}

然后实际连接,你可以在默认构造之后执行一个connect方法,可以显式调用,或者由剩下的构造函数调用:

bool Socket::connect(std::string ip, int port) {
    mSock = socket(AF_INET, SOCK_STREAM, 0);
    if (mSock == INVALID_SOCKET)
        return false;
    SOCKADDR_IN addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
    if (connect(mSock, reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr))
        == SOCKET_ERROR)
        return false;
    std::cout << mSock << " connected" << std::endl;
    return true;
}

Socket::Socket(std::string ip, int port) {
    if (!connect(ip, port))
        throw std::runtime_error("socket()");
}