NAIUI / C++必知必会

Created Wed, 03 Jul 2024 01:04:37 -0700 Modified Wed, 03 Jul 2024 01:04:37 -0700
1112 Words

C++必知必会

1、RAII

资源获取即初始化:指资源在我们拿到时就已经初始化,一旦不再需要该资源,就可以自动释放该资源。

#include <winsock2.h>
#include <stdio.h>

// 链接windows的socket网络库
#pragma comment(lib, "ws2_32.lib")

class ServerSocket
{
pubic:
    ServerSocket()
    {
		m_bInit = false;
        m_ListenSocket = -1;
    }
    
    ~ServerSocket()
    {
		if (m_ListenSocket != -1) ::closesocket(m_ListenSocket);
        if (m_bInit) ::WSACleanup();
    }
    
    bool DoInit()
    {
		// 初始化socket库
        WORD wVersionRequested = MAKEWORD(2, 2);
        WSADATA wsaData;
        int err = ::WSAStartup(wVersionRequested, &wsaData);
        if (err != 0) return  false;
        if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) return false;
        
        m_bInit = true;
        
        // 创建用于监听的socket
        m_ListenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
        if (m_ListenSocket == -1) return false;
        
        return true;
    }
    
    bool DoBind(const char* ip, short port = 6000)
    {
		SOCKADDR_IN addrSrv;
        addrSrv.sin_addr.S_un.S_addr = inet_addr(ip);
        addrSrv.sin_family = AF_INET;
        addrSrv.sin_port = htons(port);
        if (::bind(m_ListenSocket, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) == -1) return false;
        
        return true;
    }
    
    bool DoListen(int backlog = 15)    
    {
		if (::listen(m_ListenSocket, backlog) == -1) return false;
        
        return true;
    }
    
    bool DoAccept()
    {
		SOCKADDR_IN addrClient;
        int len = sizeof(SOCKADDR);
        char msg[] = "HelloWorld";
        while (true)
        {
            SOCKET sockClient = ::accept(m_ListenSocket, (SOCKADDR*)&addrClient, &len);
            if (sockClient == -1) break;
            
            // 向客户端发送“HelloWorld”消息
            ::send(sockClient, msg, strlen(msg), 0);
            ::closesocket(sockClient);          
        }// end inner-while-loop
        return false;
    }
    
private:
    bool m_bInit;
    SOCKET m_ListenSocket;
}

int main(int argc, char** argv)
{
	ServerSocket serverSocket;
    if (!serverSocket.DoInit()) return false;
    if (!serverSocket.DoBind("0.0.0.0", 6000)) return false;
    if (!serverSocket.DoListen(15)) return false;
    if (!serverSocket.DoAccept()) return false;
    return 0;
}

以上代码并没有在构造函数中分配资源,而是单独使用一个DoInit方法初始化资源,并在析构函数中回收相应的资源。这样在main函数中就不用担心任何中间步骤失败而忘记释放资源了,因为一旦main函数调用结束,serverSocket对象就会自动调用其析构函数回收相应的资源。这就是RAII惯用法的原理!

严格来说,以上代码中ServerSocket的成员变量m_bInit应该被设计成类静态成员,调用 WSAStartup WSACleanup 的函数应该被设计成类的静态方法,因为它们只需在程序初始化和退出时各调用一次就可以了。

2、pimpl用法

有没有办法既能保持对外接口不变,又能尽量不暴露一些关键的成员变量和私有函数的实现方法呢

// Impl 前置声明
class Impl;

class CSocketClient
{
public:
    CSocketClient();
    ~CSocketClient();
    
public:
    void SetProxyWnd(HWND hProxyWnd);
    
    bool Init(CNetProxy* pNetProxy);
    bool Uninit();
    
    int Register(const char* pszUser, const char* pszPassword);
    void GuestLogin();
    
    BOOL IsClosed();
    BOOL Connet(int timeout = 3);
    void AddData(int cmd, const std::string& strBuffer);
    void AddData(int cmd, const char* pszBuff, int nBuffLen);
    void Close();
    
    BOOL ConnectServer(int timeout = 3);
    BOOL SendLoginMsg();
    BOOL RecvLoginMsg(int& nRet);
    BOOL Login(int& nRet);
    
private:
    Impl* m_pImpl;
}

在以上代码中,所有的关键成员变量都已经不存在了,取而代之的是一个类型为Impl的指针成员变量m_pImpl

具体采用什么名称,读者完全可以根据自己的实际情况来定,不一定非要使用“Impl”和“m_pImpl”这样的名称。

Impl 类现在对使用者完全透明,为了在CSocketClient类中引用 Impl 类,我们在SocketClient.h文件中使用了一个前置声明,然后就可以将原来属于CSocketClient类的成员变量转移到Impl类中了:

class Impl
{
public:
    Impl()
    {

    }
    
    ~Impl()
    {

    }
    
public:
    SOCKET  m_hSocket;
    ....
}

CSocketClient::CSocketClient()
{
	m_pImpl = new Impl();
}

CSocketClient::~CSocketClient()
{
	delete m_pImpl;
}

在实际开发中,由于Impl类是CSocketClient的辅助类,没有独立存在的必要,所以一般会将Impl类定义成CSocketClient的内部类。

C++11标准引入了智能指针对象,我们可以使用std::unique_ptr对象来管理上述用于隐藏具体实现的m_pImpl指针。

...
private:
	struct Impl;
	std::unique_ptr<Impl> m_pImpl;
...

修改构造函数和析构函数

CSocketClient::CSocketClient()
{
	m_pImpl.reset(new Impl());
}

CSocketClient::~CSocketClient()
{
	//delete m_pImpl;
}

C++14构造函数

CSocketClient::CSocketClient() : m_pImpl(std::make_unique<Impl>())
{
}