C++?ASIO實(shí)現(xiàn)異步套接字管理詳解
Boost ASIO(Asynchronous I/O)是一個(gè)用于異步I/O操作的C++庫(kù),該框架提供了一種方便的方式來(lái)處理網(wǎng)絡(luò)通信、多線程編程和異步操作。特別適用于網(wǎng)絡(luò)應(yīng)用程序的開發(fā),從基本的網(wǎng)絡(luò)通信到復(fù)雜的異步操作,如遠(yuǎn)程控制程序、高并發(fā)服務(wù)器等都可以使用該框架。該框架的優(yōu)勢(shì)在于其允許處理多個(gè)并發(fā)連接,而不必創(chuàng)建一個(gè)線程來(lái)管理每個(gè)連接。最重要的是ASIO是一個(gè)跨平臺(tái)庫(kù),可以運(yùn)行在任何支持C++的平臺(tái)下。
本章筆者將介紹如何通過(guò)ASIO框架實(shí)現(xiàn)一個(gè)簡(jiǎn)單的異步網(wǎng)絡(luò)套接字應(yīng)用程序,該程序支持對(duì)Socket套接字的存儲(chǔ),默認(rèn)將套接字放入到一個(gè)Map容器內(nèi),當(dāng)需要使用時(shí)只需要將套接字在容器內(nèi)取出并實(shí)現(xiàn)通信,客戶端下線時(shí)則自動(dòng)從Map容器內(nèi)移除,通過(guò)對(duì)本章知識(shí)的學(xué)習(xí)讀者可以很容易的構(gòu)建一個(gè)跨平臺(tái)的簡(jiǎn)單遠(yuǎn)控功能。
AsyncTcpClient 異步客戶端
如下這段代碼實(shí)現(xiàn)了一個(gè)基本的帶有自動(dòng)心跳檢測(cè)的客戶端,它可以通過(guò)異步連接與服務(wù)器進(jìn)行通信,并根據(jù)不同的命令返回不同的數(shù)據(jù)。代碼邏輯較為簡(jiǎn)單,但為了保證可靠性和穩(wěn)定性,實(shí)際應(yīng)用中需要進(jìn)一步優(yōu)化、處理錯(cuò)誤和異常情況,以及增加更多的功能和安全性措施。
首先我們封裝實(shí)現(xiàn)AsyncConnect
類,該類內(nèi)主要實(shí)現(xiàn)兩個(gè)功能,其中aysnc_connect()
方法用于實(shí)現(xiàn)異步連接到服務(wù)端,而port_is_open()
方法則用于驗(yàn)證服務(wù)器特定端口是否開放,如果開放則說(shuō)明服務(wù)端還在線,不開放則說(shuō)明服務(wù)端離線此處嘗試等待一段時(shí)間后再次驗(yàn)證,在調(diào)用boost::bind()
函數(shù)綁定套接字時(shí)通過(guò)&AsyncConnect::timer_handle()
函數(shù)來(lái)設(shè)置一個(gè)超時(shí)等待時(shí)間。
進(jìn)入到主函數(shù)中,首先程序通過(guò)while
循環(huán)讓程序保持持續(xù)運(yùn)行,并通過(guò)hander.aysnc_connect(ep, 5000)
每隔5秒驗(yàn)證是否與服務(wù)端連接成功,如果連接了則進(jìn)入內(nèi)循環(huán),在內(nèi)循環(huán)中通過(guò)hander.port_is_open("127.0.0.1", 10000, 5000)
驗(yàn)證特定端口是否開放,這主要是為了保證服務(wù)端斷開后客戶端依然能夠跳轉(zhuǎn)到外部循環(huán)繼續(xù)等待服務(wù)端上線。而當(dāng)客戶端與服務(wù)端建立連接后則會(huì)持續(xù)在內(nèi)循環(huán)中socket.read_some()
接收服務(wù)端傳來(lái)的特定命令,以此來(lái)執(zhí)行不同的操作。
#define BOOST_BIND_GLOBAL_PLACEHOLDERS #include <iostream> #include <string> #include <boost/asio.hpp> #include <boost/bind.hpp> #include <boost/array.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp> #include <boost/noncopyable.hpp> using namespace std; using boost::asio::ip::tcp; // 異步連接地址與端口 class AsyncConnect { public: AsyncConnect(boost::asio::io_service& ios, tcp::socket &s) :io_service_(ios), timer_(ios), socket_(s) {} // 異步連接 bool aysnc_connect(const tcp::endpoint &ep, int million_seconds) { bool connect_success = false; // 異步連接,當(dāng)連接成功后將觸發(fā) connect_handle 函數(shù) socket_.async_connect(ep, boost::bind(&AsyncConnect::connect_handle, this, _1, boost::ref(connect_success))); // 設(shè)置一個(gè)定時(shí)器 million_seconds timer_.expires_from_now(boost::posix_time::milliseconds(million_seconds)); bool timeout = false; // 異步等待 如果超時(shí)則執(zhí)行 timer_handle timer_.async_wait(boost::bind(&AsyncConnect::timer_handle, this, _1, boost::ref(timeout))); do { // 等待異步操作完成 io_service_.run_one(); // 判斷如果timeout沒(méi)超時(shí),或者是連接建立了,則不再等待 } while (!timeout && !connect_success); timer_.cancel(); return connect_success; } // 驗(yàn)證服務(wù)器端口是否開放 bool port_is_open(std::string address, int port, int timeout) { try { boost::asio::io_service io; tcp::socket socket(io); AsyncConnect hander(io, socket); tcp::endpoint ep(boost::asio::ip::address::from_string(address), port); if (hander.aysnc_connect(ep, timeout)) { io.run(); io.reset(); return true; } else { return false; } } catch (...) { return false; } } private: // 如果連接成功了,則 connect_success = true void connect_handle(boost::system::error_code ec, bool &connect_success) { if (!ec) { connect_success = true; } } // 定時(shí)器超時(shí)timeout = true void timer_handle(boost::system::error_code ec, bool &timeout) { if (!ec) { socket_.close(); timeout = true; } } boost::asio::io_service &io_service_; boost::asio::deadline_timer timer_; tcp::socket &socket_; }; int main(int argc, char * argv[]) { try { boost::asio::io_service io; tcp::socket socket(io); AsyncConnect hander(io, socket); boost::system::error_code error; tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 10000); // 循環(huán)驗(yàn)證是否在線 go_: while (1) { // 驗(yàn)證是否連接成功,并定義超時(shí)時(shí)間為5秒 if (hander.aysnc_connect(ep, 5000)) { io.run(); std::cout << "已連接到服務(wù)端." << std::endl; // 循環(huán)接收命令 while (1) { // 驗(yàn)證地址端口是否開放,默認(rèn)等待5秒 bool is_open = hander.port_is_open("127.0.0.1", 10000, 5000); // 客戶端接收數(shù)據(jù)包 boost::array<char, 4096> buffer = { 0 }; // 如果在線則繼續(xù)執(zhí)行 if (is_open == true) { socket.read_some(boost::asio::buffer(buffer), error); // 判斷收到的命令是否為GetCPU if (strncmp(buffer.data(), "GetCPU", strlen("GetCPU")) == 0) { std::cout << "獲取CPU參數(shù)并返回給服務(wù)端." << std::endl; socket.write_some(boost::asio::buffer("CPU: 15 %")); } // 判斷收到的命令是否為GetMEM if (strncmp(buffer.data(), "GetMEM", strlen("GetMEM")) == 0) { std::cout << "獲取MEM參數(shù)并返回給服務(wù)端." << std::endl; socket.write_some(boost::asio::buffer("MEM: 78 %")); } // 判斷收到的命令是否為終止程序 if (strncmp(buffer.data(), "Exit", strlen("Exit")) == 0) { std::cout << "終止客戶端." << std::endl; return 0; } } else { // 如果連接失敗,則跳轉(zhuǎn)到等待環(huán)節(jié) goto go_; } } } else { std::cout << "連接失敗,正在重新連接." << std::endl; } } } catch (...) { return false; } std::system("pause"); return 0; }
AsyncTcpServer 異步服務(wù)端
接著我們來(lái)實(shí)現(xiàn)異步TCP服務(wù)器,首先我們需要封裝實(shí)現(xiàn)CAsyncTcpServer
類,該類使用了多線程來(lái)支持異步通信,每個(gè)客戶端連接都會(huì)創(chuàng)建一個(gè)CTcpConnection
類的實(shí)例來(lái)處理具體的通信操作,該服務(wù)器類在連接建立、數(shù)據(jù)傳輸和連接斷開時(shí),都會(huì)通過(guò)事件處理器來(lái)通知相關(guān)操作,以支持服務(wù)器端的業(yè)務(wù)邏輯。其頭文件聲明如下所示;
#ifdef _MSC_VER #define BOOST_BIND_GLOBAL_PLACEHOLDERS #define _WIN32_WINNT 0x0601 #define _CRT_SECURE_NO_WARNINGS #endif #pragma once #include <thread> #include <array> #include <boost\bind.hpp> #include <boost\noncopyable.hpp> #include <boost\asio.hpp> #include <boost\asio\placeholders.hpp> using namespace boost::asio; using namespace boost::asio::ip; using namespace boost::placeholders; using namespace std; // 每一個(gè)套接字連接,都自動(dòng)對(duì)應(yīng)一個(gè)Tcp客戶端連接 class CTcpConnection { public: CTcpConnection(io_service& ios, int clientId) : m_socket(ios), m_clientId(clientId){} ~CTcpConnection(){} int m_clientId; tcp::socket m_socket; array<BYTE, 16 * 1024> m_buffer; }; typedef shared_ptr<CTcpConnection> TcpConnectionPtr; class CAsyncTcpServer { public: class IEventHandler { public: IEventHandler(){} virtual ~IEventHandler(){} virtual void ClientConnected(int clientId) = 0; virtual void ClientDisconnect(int clientId) = 0; virtual void ReceiveData(int clientId, const BYTE* data, size_t length) = 0; }; public: CAsyncTcpServer(int maxClientNumber, int port); ~CAsyncTcpServer(); void AddEventHandler(IEventHandler* pHandler){ m_EventHandlers.push_back(pHandler); } void Send(int clientId, const BYTE* data, size_t length); string GetRemoteAddress(int clientId); string GetRemotePort(int clientId); private: void bind_hand_read(CTcpConnection* client); void handle_accept(const boost::system::error_code& error); void handle_read(CTcpConnection* client, const boost::system::error_code& error, size_t bytes_transferred); private: thread m_thread; io_service m_ioservice; io_service::work m_work; tcp::acceptor m_acceptor; int m_maxClientNumber; int m_clientId; TcpConnectionPtr m_nextClient; map<int, TcpConnectionPtr> m_clients; vector<IEventHandler*> m_EventHandlers; };
接著來(lái)實(shí)現(xiàn)AsyncTcpServer
頭文件中的功能函數(shù),此功能函數(shù)的實(shí)現(xiàn)如果讀者不明白原理可自行將其提交給ChatGPT解析,這里就不再解釋功能了。
// By: 朱迎春 (基礎(chǔ)改進(jìn)版) #include "AsyncTcpServer.h" // CAsyncTcpServer的實(shí)現(xiàn) CAsyncTcpServer::CAsyncTcpServer(int maxClientNumber, int port) : m_ioservice() , m_work(m_ioservice) , m_acceptor(m_ioservice) , m_maxClientNumber(maxClientNumber) , m_clientId(0) { m_thread = thread((size_t(io_service::*)())&io_service::run, &m_ioservice); m_nextClient = make_shared<CTcpConnection>(m_ioservice, m_clientId); m_clientId++; tcp::endpoint endpoint(tcp::v4(), port); m_acceptor.open(endpoint.protocol()); m_acceptor.set_option(tcp::acceptor::reuse_address(true)); m_acceptor.bind(endpoint); m_acceptor.listen(); // 異步等待客戶端連接 m_acceptor.async_accept(m_nextClient->m_socket, boost::bind(&CAsyncTcpServer::handle_accept, this, boost::asio::placeholders::error)); } CAsyncTcpServer::~CAsyncTcpServer() { for (map<int, TcpConnectionPtr>::iterator it = m_clients.begin(); it != m_clients.end(); ++it) { it->second->m_socket.close(); } m_ioservice.stop(); m_thread.join(); } // 根據(jù)ID號(hào)同步給特定客戶端發(fā)送數(shù)據(jù)包 void CAsyncTcpServer::Send(int clientId, const BYTE* data, size_t length) { map<int, TcpConnectionPtr>::iterator it = m_clients.find(clientId); if (it == m_clients.end()) { return; } it->second->m_socket.write_some(boost::asio::buffer(data, length)); } // 根據(jù)ID號(hào)返回客戶端IP地址 string CAsyncTcpServer::GetRemoteAddress(int clientId) { map<int, TcpConnectionPtr>::iterator it = m_clients.find(clientId); if (it == m_clients.end()) { return "0.0.0.0"; } std::string remote_address = it->second->m_socket.remote_endpoint().address().to_string(); return remote_address; } // 根據(jù)ID號(hào)返回端口號(hào) string CAsyncTcpServer::GetRemotePort(int clientId) { map<int, TcpConnectionPtr>::iterator it = m_clients.find(clientId); char ref[32] = { 0 }; if (it == m_clients.end()) { return "*"; } unsigned short remote_port = it->second->m_socket.remote_endpoint().port(); std::string str = _itoa(remote_port, ref, 10); return str; } void CAsyncTcpServer::handle_accept(const boost::system::error_code& error) { if (!error) { // 判斷連接數(shù)目是否達(dá)到最大限度 if (m_maxClientNumber > 0 && m_clients.size() >= m_maxClientNumber) { m_nextClient->m_socket.close(); } else { // 發(fā)送客戶端連接的消息 for (int i = 0; i < m_EventHandlers.size(); ++i) { m_EventHandlers[i]->ClientConnected(m_nextClient->m_clientId); } // 設(shè)置異步接收數(shù)據(jù) bind_hand_read(m_nextClient.get()); // 將客戶端連接放到客戶表中 m_clients.insert(make_pair(m_nextClient->m_clientId, m_nextClient)); // 重置下一個(gè)客戶端連接 m_nextClient = make_shared<CTcpConnection>(m_ioservice, m_clientId); m_clientId++; } } // 異步等待下一個(gè)客戶端連接 m_acceptor.async_accept(m_nextClient->m_socket, boost::bind(&CAsyncTcpServer::handle_accept, this, boost::asio::placeholders::error)); } void CAsyncTcpServer::bind_hand_read(CTcpConnection* client) { client->m_socket.async_read_some(boost::asio::buffer(client->m_buffer), boost::bind(&CAsyncTcpServer::handle_read, this, client, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); return; client->m_socket.async_receive(boost::asio::buffer(client->m_buffer), boost::bind(&CAsyncTcpServer::handle_read, this, client, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); boost::asio::async_read(client->m_socket, boost::asio::buffer(client->m_buffer), boost::bind(&CAsyncTcpServer::handle_read, this, client, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } void CAsyncTcpServer::handle_read(CTcpConnection* client, const boost::system::error_code& error, size_t bytes_transferred) { if (!error) { // 發(fā)送收到數(shù)據(jù)的信息 for (int i = 0; i < m_EventHandlers.size(); ++i) { m_EventHandlers[i]->ReceiveData(client->m_clientId, client->m_buffer.data(), bytes_transferred); } bind_hand_read(client); } else { // 發(fā)送客戶端離線的消息 for (int i = 0; i < m_EventHandlers.size(); ++i) { m_EventHandlers[i]->ClientDisconnect(client->m_clientId); } m_clients.erase(client->m_clientId); } }
AsyncTcpServer 類調(diào)用
服務(wù)端首先定義CEventHandler
類并繼承自CAsyncTcpServer::IEventHandler
接口,該類內(nèi)需要我們實(shí)現(xiàn)三個(gè)方法,方法ClientConnected
用于在客戶端連接時(shí)觸發(fā),方法ClientDisconnect
則是在登錄客戶端離開時(shí)觸發(fā),而當(dāng)客戶端有數(shù)據(jù)發(fā)送過(guò)來(lái)時(shí)則ReceiveData
方法則會(huì)被觸發(fā)。
方法ClientConnected
當(dāng)被觸發(fā)時(shí)自動(dòng)將clientId
客戶端Socket套接字放入到tcp_client_id
全局容器內(nèi)存儲(chǔ)起來(lái),而當(dāng)ClientDisconnect
客戶端退出時(shí),則直接遍歷這個(gè)迭代容器,找到序列號(hào)并通過(guò)tcp_client_id.erase
將其剔除;
// 客戶端連接時(shí)觸發(fā) virtual void ClientConnected(int clientId) { // 將登錄客戶端加入到容器中 tcp_client_id.push_back(clientId); } // 客戶端退出時(shí)觸發(fā) virtual void ClientDisconnect(int clientId) { // 將登出的客戶端從容器中移除 vector<int>::iterator item = find(tcp_client_id.begin(), tcp_client_id.end(), clientId); if (item != tcp_client_id.cend()) tcp_client_id.erase(item); }
而ReceiveData
一旦收到數(shù)據(jù),則直接將其打印輸出到屏幕,即可實(shí)現(xiàn)客戶端參數(shù)接收的目的;
// 客戶端獲取數(shù)據(jù) virtual void ReceiveData(int clientId, const BYTE* data, size_t length) { std::cout << std::endl; PrintLine(80); std::cout << data << std::endl; PrintLine(80); std::cout << "[Shell] # "; }
相對(duì)于接收數(shù)據(jù)而言,發(fā)送數(shù)據(jù)則是通過(guò)同步的方式進(jìn)行,當(dāng)我們需要發(fā)送數(shù)據(jù)時(shí),只需要將數(shù)據(jù)字符串放入到一個(gè)BYTE*
字節(jié)數(shù)組中,并在調(diào)用tcpServer.Send
時(shí)將所需參數(shù),套接字ID,緩沖區(qū)Buf數(shù)據(jù),以及長(zhǎng)度傳遞即可實(shí)現(xiàn)將數(shù)據(jù)發(fā)送給指定的客戶端;
// 同步發(fā)送數(shù)據(jù)到指定的線程中 void send_message(CAsyncTcpServer& tcpServer, int clientId, std::string message, int message_size) { // 獲取長(zhǎng)度 BYTE* buf = new BYTE(message_size + 1); memset(buf, 0, message_size + 1); for (int i = 0; i < message_size; i++) { buf[i] = message.at(i); } tcpServer.Send(clientId, buf, message_size); }
客戶端完整代碼如下所示,運(yùn)行客戶端后讀者可自行使用不同的命令來(lái)接收參數(shù)返回值;
#include "AsyncTcpServer.h" #include <string> #include <vector> #include <iostream> #include <boost/tokenizer.hpp> using namespace std; // 存儲(chǔ)當(dāng)前客戶端的ID號(hào) std::vector<int> tcp_client_id; // 輸出特定長(zhǎng)度的行 void PrintLine(int line) { for (int x = 0; x < line; x++) { printf("-"); } printf("\n"); } class CEventHandler : public CAsyncTcpServer::IEventHandler { public: // 客戶端連接時(shí)觸發(fā) virtual void ClientConnected(int clientId) { // 將登錄客戶端加入到容器中 tcp_client_id.push_back(clientId); } // 客戶端退出時(shí)觸發(fā) virtual void ClientDisconnect(int clientId) { // 將登出的客戶端從容器中移除 vector<int>::iterator item = find(tcp_client_id.begin(), tcp_client_id.end(), clientId); if (item != tcp_client_id.cend()) tcp_client_id.erase(item); } // 客戶端獲取數(shù)據(jù) virtual void ReceiveData(int clientId, const BYTE* data, size_t length) { std::cout << std::endl; PrintLine(80); std::cout << data << std::endl; PrintLine(80); std::cout << "[Shell] # "; } }; // 同步發(fā)送數(shù)據(jù)到指定的線程中 void send_message(CAsyncTcpServer& tcpServer, int clientId, std::string message, int message_size) { // 獲取長(zhǎng)度 BYTE* buf = new BYTE(message_size + 1); memset(buf, 0, message_size + 1); for (int i = 0; i < message_size; i++) { buf[i] = message.at(i); } tcpServer.Send(clientId, buf, message_size); } int main(int argc, char* argv[]) { CAsyncTcpServer tcpServer(10, 10000); CEventHandler eventHandler; tcpServer.AddEventHandler(&eventHandler); std::string command; while (1) { std::cout << "[Shell] # "; std::getline(std::cin, command); if (command.length() == 0) { continue; } else if (command == "help") { printf(" _ ____ _ _ \n"); printf("| | _ _ / ___| ___ ___| | _____| |_ \n"); printf("| | | | | | \\___ \\ / _ \\ / __| |/ / _ \\ __| \n"); printf("| |__| |_| | ___) | (_) | (__| < __/ |_ \n"); printf("|_____\\__, | |____/ \\___/ \\___|_|\\_\\___|\\__| \n"); printf(" |___/ \n\n"); printf("Usage: LySocket \t PowerBy: LyShark.com \n"); printf("Optional: \n\n"); printf("\t ShowSocket 輸出所有Socket容器 \n"); printf("\t GetCPU 獲取CPU數(shù)據(jù) \n"); printf("\t GetMemory 獲取內(nèi)存數(shù)據(jù) \n"); printf("\t Exit 退出客戶端 \n\n"); } else { // 定義分詞器: 定義分割符號(hào)為[逗號(hào),空格] boost::char_separator<char> sep(", --"); typedef boost::tokenizer<boost::char_separator<char>> CustonTokenizer; CustonTokenizer tok(command, sep); // 將分詞結(jié)果放入vector鏈表 std::vector<std::string> vecSegTag; for (CustonTokenizer::iterator beg = tok.begin(); beg != tok.end(); ++beg) { vecSegTag.push_back(*beg); } // 解析 [shell] # ShowSocket if (vecSegTag.size() == 1 && vecSegTag[0] == "ShowSocket") { PrintLine(80); printf("客戶ID \t 客戶IP地址 \t 客戶端口 \n"); PrintLine(80); for (int x = 0; x < tcp_client_id.size(); x++) { std::cout << tcp_client_id[x] << " \t " << tcpServer.GetRemoteAddress(tcp_client_id[x]) << " \t " << tcpServer.GetRemotePort(tcp_client_id[x]) << std::endl; } PrintLine(80); } // 解析 [shell] # GetCPU --id 100 if (vecSegTag.size() == 3 && vecSegTag[0] == "GetCPU") { char *id = (char *)vecSegTag[2].c_str(); send_message(tcpServer, atoi(id), "GetCPU", strlen("GetCPU")); } // 解析 [shell] # GetMemory --id 100 if (vecSegTag.size() == 3 && vecSegTag[0] == "GetMemory") { char* id = (char*)vecSegTag[2].c_str(); send_message(tcpServer, atoi(id), "GetMEM", strlen("GetMEM")); } // 解析 [shell] # Exit --id 100 if (vecSegTag.size() == 3 && vecSegTag[0] == "Exit") { char* id = (char*)vecSegTag[2].c_str(); send_message(tcpServer, atoi(id), "Exit", strlen("Exit")); } } } return 0; }
案例演示
首先運(yùn)行服務(wù)端程序,接著運(yùn)行多個(gè)客戶端,即可實(shí)現(xiàn)自動(dòng)上線;
當(dāng)用戶需要通信時(shí),只需要指定id序號(hào)到指定的Socket套接字編號(hào)即可;
以上就是C++ ASIO實(shí)現(xiàn)異步套接字管理詳解的詳細(xì)內(nèi)容,更多關(guān)于C++ ASIO異步套接字管理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C 語(yǔ)言基礎(chǔ)之C 語(yǔ)言三大語(yǔ)句注意事項(xiàng)
今天講解的內(nèi)容,則是自己對(duì)于這三種語(yǔ)句一些細(xì)節(jié)的簡(jiǎn)單介紹,分支語(yǔ)句:if,switch、循環(huán)語(yǔ)句:while,for,do while、goto語(yǔ)句,感興趣的小伙伴可以參考下面具體的文章內(nèi)容2021-09-09C++中獲取UTC時(shí)間精確到微秒的實(shí)現(xiàn)代碼
本篇文章是對(duì)C++中獲取UTC時(shí)間精確到微秒的實(shí)現(xiàn)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C/C++中指針和引用之相關(guān)問(wèn)題深入研究
從內(nèi)存分配上看,程序?yàn)橹羔樧兞糠峙鋬?nèi)存區(qū)域,而不為引用分配內(nèi)存區(qū)域,因?yàn)橐寐暶鲿r(shí)必須初始化,從而指向一個(gè)已經(jīng)存在的對(duì)象。引用不能指向空值2013-10-10C++高級(jí)數(shù)據(jù)結(jié)構(gòu)之并查集
這篇文章主要介紹了C高級(jí)數(shù)據(jù)結(jié)構(gòu)之并查集,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05C++深入淺出探索數(shù)據(jù)結(jié)構(gòu)的原理
C++的數(shù)據(jù)結(jié)構(gòu)很多,很復(fù)雜,所以本文將通過(guò)示例帶大家深入了解一下C++中的數(shù)據(jù)結(jié)構(gòu)與算法。文中的示例代碼講解詳細(xì),感興趣的可以了解一下2022-05-05C++實(shí)現(xiàn)正態(tài)隨機(jī)分布的方法
本篇介紹了,使用c++實(shí)現(xiàn)正態(tài)隨機(jī)分布的實(shí)現(xiàn)方法。需要的朋友參考下2013-05-05C++實(shí)現(xiàn)LeetCode(33.在旋轉(zhuǎn)有序數(shù)組中搜索)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(33.在旋轉(zhuǎn)有序數(shù)組中搜索),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07