代码书写
代码书写规范基本遵循 谷歌代码书写规范 ,中文版 ;
不使用using namespace std
,不使用goto
,不使用vector<bool>
,使用using
代替typedef
;
谨慎使用宏定义(尽可能使用const
代替宏),#
代表将宏参数内容转换成字符串,##
代表将两个参数内容连接成为一个字符串;
一个可参考的 C++
工程目录结构:https://github.com/hattonl/cpp-project-structure
一些常见的注释约定:// TODO
(待办),// FIXME
(待修复),// HACK
(临时解决方案),// NOTE
(备注),// OPTIMIZE
(待优化),// REVIEW
(待审查),// DEPRECATED
(已过时);
ErrorMessage.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #ifndef ERRORMESSAGE_H #define ERRORMESSAGE_H #include <string> #include <iostream> #define make_error_message(message) make_error_message_raw(__FILE__, __FUNCTION__, __LINE__, (message)) static std::string make_error_message_raw (std::string file, std::string function, int line, std::string message) { return file + ":\n|\n|----" + function + " (line " + std::to_string (line) + "): \n |\n |---- error: " + message + "\n" ; } #define die_if_error(condition_true, output_message) \ ((condition_true) ? 0 : (std::cerr << (output_message), exit(1), 1)) #endif
pImpl
Pointer to
implementation 是一种编程技巧。
场景一:某个头文件定义了一个类的各项成员与虚函数,如果此后这个头文件被修改,其对应的
ABI
将改变,从而只能重新编译此头文件及其依赖项。我们希望不将类的具体定义放在头文件中,从而加速编译,并获得更好的
ABI 稳定性;
场景二:在某个头文件中,一个类声明了另一个文件定义的类的指针作为其成员变量。当我们不希望引入对应头文件(可能造成头文件泄露)时,我们需要使用前向声明class Type;
,但是 C++11
之后的智能指针unique_ptr
不支持不完整类型(因为需要对应类型的各项信息才能帮助释放),因此我们不希望将类成员变量定义直接放入头文件中;
在这两个情形中,我们都不希望在头文件中定义类的具体信息。于是我们在头文件的类中定义一个嵌套类,所有原先类的成员变量和函数,都放入嵌套类中。头文件仅声明一个指向该嵌套类的unique_ptr
,具体嵌套类的成员变量和函数均放入
.cpp 源文件中实现。
为了实现这一点, unique_ptr
要求我们在头文件中显式声明外层类的特殊成员函数(构造、析构、拷贝构造、拷贝赋值、移动构造、移动赋值),并且如果不禁用某函数(即希望自定义函数或使用= default
,不使用= delete
),则必须在源文件中定义(无法直接在头文件中定义一个不完全类型的成员函数)。具体实现见上方官方链接。
在官方示例中,使用了目前还未标准化的std::experimental::propagate_const<std::unique_ptr<impl>> pImpl;
,它能够向内传递指针对象的常量性。通常情况下,无法通过一个指向常量的指针(pointer
to
const)修改它指向对象的成员变量,也无法调用它的变量成员函数。但使用嵌套类后,可以通过调用
此指针指向的对象 内的 指向嵌套类对象的指针 指向的对象 的 成员函数
间接修改那个嵌套类。举例:object_ptr->pImpl->var_func()
。也就是说,嵌套类不传递常量性,因此必须通过此方法实现常量性的正确传递。
C++ 实现多客户端 TCP 通信
参考:
C++并发编程(C++11到C++17)
Socket
编程详解
服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 #include <iostream> #include <thread> #include <mutex> #include <vector> #include <string> #include <cstring> #include <unistd.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> const int IP_PORT = 8888 ;const int MAX_CLIENTS = 10 ;const int BUFFER_SIZE = 1024 ;std::mutex mtx; std::vector<int > clients; void handle_client (int client_socket) { char buffer[BUFFER_SIZE]; while (true ) { std::cout << "------------------------------" << std::endl; memset (buffer, 0 , BUFFER_SIZE); int bytes_read = recv (client_socket, buffer, BUFFER_SIZE, 0 ); if (bytes_read <= 0 ) { std::cerr << "Failed to receive data from client" << std::endl; close (client_socket); break ; } buffer[bytes_read] = '\0' ; std::cout << "Received " << bytes_read << " bytes: " << buffer << std::endl; std::string message (buffer) ; { std::lock_guard<std::mutex> lock (mtx) ; for (auto &client: clients) { int num_bytes_sent = send (client, message.c_str (), message.length (), 0 ); if (num_bytes_sent < 0 ) { std::cerr << "Failed to send data to client" << std::endl; close (client_socket); continue ; } std::cout << "Sent " << message.length () << " bytes: " << message << std::endl; } } } close (client_socket); { std::lock_guard<std::mutex> lock (mtx) ; clients.erase (std::remove (clients.begin (), clients.end (), client_socket), clients.end ()); } } int main () { int server_socket = socket (AF_INET, SOCK_STREAM, 0 ); if (server_socket < 0 ) { std::cerr << "Failed to create socket" << std::endl; return 1 ; } sockaddr_in server_address{}; server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = htonl (INADDR_ANY); server_address.sin_port = htons (IP_PORT); if (bind (server_socket, (sockaddr *)&server_address, sizeof (server_address)) < 0 ) { std::cerr << "Failed to bind socket to local address and port" << std::endl; close (server_socket); return 1 ; } if (listen (server_socket, MAX_CLIENTS) < 0 ) { std::cerr << "Failed to listen on socket" << std::endl; close (server_socket); return 1 ; } std::cout << "Listening on 0.0.0.0:" << IP_PORT << std::endl; while (true ) { sockaddr_in client_address{}; socklen_t client_address_size = sizeof (client_address); int client_socket = accept (server_socket, (sockaddr*)&client_address, &client_address_size); if (client_socket < 0 ) { std::cerr << "Error: Failed to accept client connection." << std::endl; close (server_socket); continue ; } std::cout << "Accepted client connection from " << inet_ntoa (client_address.sin_addr) << ":" << ntohs (client_address.sin_port) << std::endl; { std::lock_guard<std::mutex> lock (mtx) ; clients.push_back (client_socket); } std::cout << "New client connected: " << inet_ntoa (client_address.sin_addr) << ":" << ntohs (client_address.sin_port) << "\n" ; std::thread (handle_client, client_socket).detach (); } close (server_socket); return 0 ; }
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <string> #include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> const int IP_PORT = 8888 ;const int BUFFER_SIZE = 1024 ;int main () { int client_socket = socket (AF_INET, SOCK_STREAM, 0 ); if (client_socket < 0 ) { std::cerr << "Failed to create socket" << std::endl; return 1 ; } struct sockaddr_in server_address; std::memset (&server_address, 0 , sizeof (server_address)); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = inet_addr ("127.0.0.1" ); server_address.sin_port = htons (IP_PORT); if (connect (client_socket, (struct sockaddr *)&server_address, sizeof (server_address)) < 0 ) { std::cerr << "Failed to connect to server" << std::endl; close (client_socket); return 1 ; } std::cout << "Connected to server at " << inet_ntoa (server_address.sin_addr) << ":" << ntohs (server_address.sin_port) << std::endl; while (true ){ std::cout << "------------------------------" << std::endl; std::string request_message; std::cout << "Input the message you want to send to server" << std::endl; std::getline (std::cin, request_message); if (request_message == "exit" ){ std::cout << "Bye" << std::endl; break ; } int num_bytes_sent = send (client_socket, request_message.c_str (), request_message.length () , 0 ); if (num_bytes_sent < 0 ) { std::cerr << "Failed to send data to server" << std::endl; close (client_socket); return 1 ; } std::cout << "Sent " << num_bytes_sent << " bytes: " << request_message << std::endl; char buffer[BUFFER_SIZE]; int num_bytes_received = recv (client_socket, buffer, sizeof (buffer), 0 ); if (num_bytes_received < 0 ) { std::cerr << "Failed to receive data from server" << std::endl; close (client_socket); return 1 ; } buffer[num_bytes_received] = '\0' ; std::cout << "Received " << num_bytes_received << " bytes: " << buffer << std::endl; } close (client_socket); return 0 ; }