1.C++连接和 *** 作MysqL的方式
系列文章:
MySQL 设计和命令行模式下建立详解
C++利用MySQL API连接和 *** 作数据库实例详解
在windows平台,我们可以使用ADO、ODBC或者MysqL API进行连接和 *** 作。ADO (ActiveX Data Objects,ActiveX数据对象)是Microsoft提出的一个用于存取数据源的COM组件。它提供了程序语言和统一数据访问方式olE DB的一个中间层,也就是Microsoft提出的应用程序接口(API)用以实现访问关系或非关系数据库中的数据。
ODBC(Open DataBase Connection)开放式系统互连,是一种数据库访问协议,提供了访问数据库的API接口。基于ODBC的应用程序,对数据库 *** 作不依赖于具体的DBMS,不直接与DBMS打交道,所有数据库 *** 作由对应DBMS的ODBC驱动程序完成,即:系统中不需要安装DBMS系统,如sql SERVER 2005,但必须有sql SERVER 2005的ODBC驱动程序,然后在ODBC管理器中注册数据源后,就可以在应用程序中通过ODBC API访问该数据库。ODBC数据库访问技术只适用于windows系统,因为需要在ODBC驱动程序管理器中进行数据源注册,而只有windows才集成了ODBC驱动程序管理器(“控制面板/管理工具/数据源”)。
ADO具有跨系统平台特性,它直接对DBMS数据库进行 *** 作,即系统中必须有DBMS,但不需要驱动程序,不需要注册数据源,所以具有很好的可移植性。
那么,在linux平台如何连接和使用Msql数据库呢?我们同样可以使用ADO、unixODBC或者MysqL API。这里不再赘述前两者的用法,读者可自行研究实践,下文将详细讲解MysqL创建数据库和C++利用Msql API连接和 *** 作数据库。
2.Msql数据库的设计和建立
MysqL数据库管理系统(DBMS)中,包含的MysqL中定义数据字段的类型对你数据库的优化是非常重要的。MysqL支持多种类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。
本文以大学熟悉的学生选课管理系统中用到的数据库为例,来实现对数据库的访问。本文数据库的建立,是在linux平台使用msyql命令完成
主要有三张表:学生表,课程表和选课表。下面是数据表的详细情况。
学生表:
课程表:
选课表:
3.Msql数据库的连接和 *** 作
下面将讲解利用MysqL API来编写我们自己的用于访问MysqL的中间件,也是我们自己的组件。我们的组件在应用程序和MysqL数据库之间构成的层次结构如下图所示:
下面就来设计和实现我们自己的C++访问MysqL数据库的组件。
3.1头文件的设计
//MysqLhelper.h#ifndef __MysqL_HELPER_H__#define __MysqL_HELPER_H__#include <stdlib.h>#include <map>#include <vector>#include <string>using namespace std;#include <MysqL.h>namespace MysqLhelper{/**********************@brIEf 数据库异常类**********************/struct MysqLHelper_Exception //: public TC_Exception{ MysqLHelper_Exception(const string &sBuffer):errorInfo(sBuffer){}; //: TC_Exception(sBuffer){}; ~MysqLHelper_Exception() throw(){}; string errorInfo;};/************************ @brIEf 数据库配置接口***********************/struct DBConf{ string _host;//主机地址 string _user; //用户名 string _password;//密码 string _database; //数据库 string _charset; //字符集 int _port;//端口 int _flag; //客户端标识 /***************** * @brIEf 构造函数 *****************/ DBConf():_port(0),_flag(0){} /********************************** * @brIEf 读取数据库配置. * @param mpParam 存放数据库配置的map * dbhost: 主机地址 * dbuser:用户名 * dbpass:密码 * dbname:数据库名称 * dbport:端口 **********************************/ voID loadFromMap(const map<string,string> &mpParam) { map<string,string> mpTmp = mpParam; _host = mpTmp["dbhost"]; _user = mpTmp["dbuser"]; _password = mpTmp["dbpass"]; _database = mpTmp["dbname"]; _charset = mpTmp["charset"]; _port = atoi(mpTmp["dbport"].c_str()); _flag = 0; if(mpTmp["dbport"] == "") { _port = 3306; } }};/*************************************************************** @brIEf:MysqL数据库 *** 作类 * @feature:非线程安全,通常一个线程一个MysqLHelper对象;* 对于insert/update可以有更好的函数封装,保证sql注入;* MysqLHelper::DB_INT表示组装SQL语句时,不加””和转义;* MysqLHelper::DB_STR表示组装SQL语句时,加””并转义;**************************************************************/class MysqLHelper{public: /** * @brIEf 构造函数 */ MysqLHelper(); /** * @brIEf 构造函数. * @param: sHost:主机IP * @param sUser 用户 * @param sPasswd 密码 * @param sDatebase 数据库 * @param port 端口 * @param iUnixSocket socket * @param iFlag 客户端标识 */ MysqLHelper(const string& sHost,const string& sUser = "",const string& sPasswd = "",const string& sDatabase = "",const string &sCharSet = "",int port = 0,int iFlag = 0); /** * @brIEf 构造函数. * @param tcDBConf 数据库配置 */ MysqLHelper(const DBConf& tcDBConf); /** * @brIEf 析构函数. */ ~MysqLHelper(); /** * @brIEf 初始化. * * @param sHost 主机IP * @param sUser 用户 * @param sPasswd 密码 * @param sDatebase 数据库 * @param port 端口 * @param iUnixSocket socket * @param iFlag 客户端标识 * @return 无 */ voID init(const string& sHost,int iFlag = 0); /** * @brIEf 初始化. * * @param tcDBConf 数据库配置 */ voID init(const DBConf& tcDBConf); /** * @brIEf 连接数据库. * * @throws MysqLHelper_Exception * @return 无 */ voID connect(); /** * @brIEf 断开数据库连接. * @return 无 */ voID disconnect(); /** * @brIEf 获取数据库变量. * @return 数据库变量 */ string getvariables(const string &sname); /** * @brIEf 直接获取数据库指针. * * @return MysqL* 数据库指针 */ MysqL *getMysqL(); /** * @brIEf 字符转义. * * @param sFrom 源字符串 * @param sTo 输出字符串 * @return 输出字符串 */ string escapestring(const string& sFrom); /** * @brIEf 更新或者插入数据. * * @param ssql SQL语句 * @throws MysqLHelper_Exception * @return */ voID execute(const string& ssql); /** * @brIEf MysqL的一条记录 */ class MysqLRecord { public: /** * @brIEf 构造函数. * * @param record */ MysqLRecord(const map<string,string> &record); /** * @brIEf 获取数据,s一般是指数据表的某个字段名 * @param s 要获取的字段 * @return 符合查询条件的记录的s字段名 */ const string& operator[](const string &s); protected: const map<string,string> &_record; }; /** * @brIEf 查询出来的MysqL数据 */ class MysqLData { public: /** * @brIEf 所有数据. * * @return vector<map<string,string>>& */ vector<map<string,string> >& data(); /** * 数据的记录条数 * * @return size_t */ size_t size(); /** * @brIEf 获取某一条记录. * * @param i 要获取第几条记录 * @return MysqLRecord类型的数据,可以根据字段获取相关信息, */ MysqLRecord operator[](size_t i); protected: vector<map<string,string> > _data; }; /** * @brIEf query Record. * * @param ssql SQL语句 * @throws MysqLHelper_Exception * @return MysqLData类型的数据,可以根据字段获取相关信息 */ MysqLData queryRecord(const string& ssql); /** * @brIEf 定义字段类型, * DB_INT:数字类型 * DB_STR:字符串类型 */ enum FT { DB_INT,DB_STR,}; /** * 数据记录 */ typedef map<string,pair<FT,string> > RECORD_DATA; /** * @brIEf 更新记录. * * @param stablename 表名 * @param mpColumns 列名/值对 * @param sCondition where子语句,例如:where A = B * @throws MysqLHelper_Exception * @return size_t 影响的行数 */ size_t updateRecord(const string &stablename,const map<string,string> > &mpColumns,const string &sCondition); /** * @brIEf 插入记录. * * @param stablename 表名 * @param mpColumns 列名/值对 * @throws MysqLHelper_Exception * @return size_t 影响的行数 */ size_t insertRecord(const string &stablename,string> > &mpColumns); /** * @brIEf 替换记录. * * @param stablename 表名 * @param mpColumns 列名/值对 * @throws MysqLHelper_Exception * @return size_t 影响的行数 */ size_t replaceRecord(const string &stablename,string> > &mpColumns); /** * @brIEf 删除记录. * * @param stablename 表名 * @param sCondition where子语句,例如:where A = B * @throws MysqLHelper_Exception * @return size_t 影响的行数 */ size_t deleteRecord(const string &stablename,const string &sCondition = ""); /** * @brIEf 获取table查询结果的数目. * * @param stablename 用于查询的表名 * @param sCondition where子语句,例如:where A = B * @throws MysqLHelper_Exception * @return size_t 查询的记录数目 */ size_t getRecordCount(const string& stablename,const string &sCondition = ""); /** * @brIEf 获取sql返回结果集的个数. * * @param sCondition where子语句,例如:where A = B * @throws MysqLHelper_Exception * @return 查询的记录数目 */ size_t getsqlCount(const string &sCondition = ""); /** * @brIEf 存在记录. * * @param sql SQL语句 * @throws MysqLHelper_Exception * @return *** 作是否成功 */ bool existRecord(const string& sql); /** * @brIEf 获取字段最大值. * * @param stablename 用于查询的表名 * @param sFIEldname 用于查询的字段 * @param sCondition where子语句,例如:where A = B * @throws MysqLHelper_Exception * @return 查询的记录数目 */ int getMaxValue(const string& stablename,const string& sFIEldname,const string &sCondition = ""); /** * @brIEf 获取auto_increment最后插入得ID. * * @return ID值 */ long lastInsertID(); /** * @brIEf 构造Insert-SQL语句. * * @param stablename 表名 * @param mpColumns 列名/值对 * @return string insert-SQL语句 */ string buildInsertsql(const string &stablename,string> > &mpColumns); /** * @brIEf 构造Replace-SQL语句. * * @param stablename 表名 * @param mpColumns 列名/值对 * @return string insert-SQL语句 */ string buildreplacesql(const string &stablename,string> > &mpColumns); /** * @brIEf 构造Update-SQL语句. * * @param stablename 表名 * @param mpColumns 列名/值对 * @param sCondition where子语句 * @return string Update-SQL语句 */ string buildUpdatesql(const string &stablename,const string &sCondition); /** * @brIEf 获取最后执行的SQL语句. * * @return SQL语句 */ string getLastsql() { return _sLastsql; } /** * @brIEf 获取查询影响数 * @return int */ size_t getAffectedRows();protected: /** * @brIEf copy contructor,只申明,不定义,保证不被使用 */ MysqLHelper(const MysqLHelper &tcMysqL); /** * * @brIEf 只申明,保证不被使用 */ MysqLHelper &operator=(const MysqLHelper &tcMysqL);private: /** * 数据库指针 */ MysqL *_pstMql; /** * 数据库配置 */ DBConf _dbConf; /** * 是否已经连接 */ bool _bConnected; /** * 最后执行的sql */ string _sLastsql;};}#endif //__MysqL_HELPER_H__
3.2源文件具体实现
//MysqLhelper.cpp#include "MysqLHelper.h"#include <string.h>#include <sstream>using namespace std;namespace MysqLhelper{MysqLHelper::MysqLHelper():_bConnected(false){ _pstMql = MysqL_init(NulL);}MysqLHelper::MysqLHelper(const string& sHost,const string& sUser,const string& sPasswd,const string& sDatabase,const string &sCharSet,int port,int iFlag):_bConnected(false){ init(sHost,sUser,sPasswd,sDatabase,sCharSet,port,iFlag); _pstMql = MysqL_init(NulL);}MysqLHelper::MysqLHelper(const DBConf& tcDBConf):_bConnected(false){ _dbConf = tcDBConf; _pstMql = MysqL_init(NulL); }MysqLHelper::~MysqLHelper(){ if (_pstMql != NulL) { MysqL_close(_pstMql); _pstMql = NulL; }}voID MysqLHelper::init(const string& sHost,int iFlag){ _dbConf._host = sHost; _dbConf._user = sUser; _dbConf._password = sPasswd; _dbConf._database = sDatabase; _dbConf._charset = sCharSet; _dbConf._port = port; _dbConf._flag = iFlag;}voID MysqLHelper::init(const DBConf& tcDBConf){ _dbConf = tcDBConf;}voID MysqLHelper::connect(){ disconnect(); if( _pstMql == NulL) { _pstMql = MysqL_init(NulL); } //建立连接后,自动调用设置字符集语句 if(!_dbConf._charset.empty()) { if (MysqL_options(_pstMql,MysqL_SET_CHARSET_name,_dbConf._charset.c_str())) { throw MysqLHelper_Exception(string("MysqLHelper::connect: MysqL_options MysqL_SET_CHARSET_name ") + _dbConf._charset + ":" + string(MysqL_error(_pstMql))); } } if (MysqL_real_connect(_pstMql,_dbConf._host.c_str(),_dbConf._user.c_str(),_dbConf._password.c_str(),_dbConf._database.c_str(),_dbConf._port,NulL,_dbConf._flag) == NulL) { throw MysqLHelper_Exception("[MysqLHelper::connect]: MysqL_real_connect: " + string(MysqL_error(_pstMql))); } _bConnected = true;}voID MysqLHelper::disconnect(){ if (_pstMql != NulL) { MysqL_close(_pstMql); _pstMql = MysqL_init(NulL); } _bConnected = false; }string MysqLHelper::escapestring(const string& sFrom){ if(!_bConnected) { connect(); } string sTo; string::size_type iLen = sFrom.length() * 2 + 1; char *pTo = (char *)malloc(iLen); memset(pTo,0x00,iLen); MysqL_real_escape_string(_pstMql,pTo,sFrom.c_str(),sFrom.length()); sTo = pTo; free(pTo); return sTo;}MysqL *MysqLHelper::getMysqL(voID){ return _pstMql;}string MysqLHelper::buildInsertsql(const string &stablename,const RECORD_DATA &mpColumns){ ostringstream sColumnnames; ostringstream sColumnValues; map<string,string> >::const_iterator itEnd = mpColumns.end(); for(map<string,string> >::const_iterator it = mpColumns.begin(); it != itEnd; ++it) { if (it == mpColumns.begin()) { sColumnnames << "`" << it->first << "`"; if(it->second.first == DB_INT) { sColumnValues << it->second.second; } else { sColumnValues << "'" << escapestring(it->second.second) << "'"; } } else { sColumnnames << ",`" << it->first << "`"; if(it->second.first == DB_INT) { sColumnValues << "," + it->second.second; } else { sColumnValues << ",'" + escapestring(it->second.second) << "'"; } } } ostringstream os; os << "insert into " << stablename << " (" << sColumnnames.str() << ") values (" << sColumnValues.str() << ")"; return os.str();}string MysqLHelper::buildreplacesql(const string &stablename,string> >::const_iterator itEnd = mpColumns.end(); for(map<string,'" << escapestring(it->second.second) << "'"; } } } ostringstream os; os << "replace into " << stablename << " (" << sColumnnames.str() << ") values (" << sColumnValues.str() << ")"; return os.str();}string MysqLHelper::buildUpdatesql(const string &stablename,const RECORD_DATA &mpColumns,const string &sWhereFilter){ ostringstream sColumnnameValueSet; map<string,string> >::const_iterator it = mpColumns.begin(); it != itEnd; ++it) { if (it == mpColumns.begin()) { sColumnnameValueSet << "`" << it->first << "`"; } else { sColumnnameValueSet << ",`" << it->first << "`"; } if(it->second.first == DB_INT) { sColumnnameValueSet << "= " << it->second.second; } else { sColumnnameValueSet << "= '" << escapestring(it->second.second) << "'"; } } ostringstream os; os << "update " << stablename << " set " << sColumnnameValueSet.str() << " " << sWhereFilter; return os.str();}string MysqLHelper::getvariables(const string &sname){ string sql = "SHOW VARIABLES liKE '" + sname + "'"; MysqLData data = queryRecord(sql); if(data.size() == 0) { return ""; } if(sname == data[0]["Variable_name"]) { return data[0]["Value"]; } return "";}voID MysqLHelper::execute(const string& ssql){ /** 没有连上,连接数据库 */ if(!_bConnected) { connect(); } _sLastsql = ssql; int iRet = MysqL_real_query(_pstMql,ssql.c_str(),ssql.length()); if(iRet != 0) { /** 自动重新连接 */ int IErrno = MysqL_errno(_pstMql); if (IErrno == 2013 || IErrno == 2006) { connect(); iRet = MysqL_real_query(_pstMql,ssql.length()); } } if (iRet != 0) { throw MysqLHelper_Exception("[MysqLHelper::execute]: MysqL_query: [ " + ssql+" ] :" + string(MysqL_error(_pstMql))); }}MysqLHelper::MysqLData MysqLHelper::queryRecord(const string& ssql){ MysqLData data; /** 没有连上,ssql.length()); } } if (iRet != 0) { throw MysqLHelper_Exception("[MysqLHelper::execute]: MysqL_query: [ " + ssql+" ] :" + string(MysqL_error(_pstMql))); } MysqL_RES *pstRes = MysqL_store_result(_pstMql); if(pstRes == NulL) { throw MysqLHelper_Exception("[MysqLHelper::queryRecord]: MysqL_store_result: " + ssql + " : " + string(MysqL_error(_pstMql))); } vector<string> vtFIElds; MysqL_FIELD *fIEld; while((fIEld = MysqL_fetch_fIEld(pstRes))) { vtFIElds.push_back(fIEld->name); } map<string,string> mpRow; MysqL_ROW stRow; while((stRow = MysqL_fetch_row(pstRes)) != (MysqL_ROW)NulL) { mpRow.clear(); unsigned long * lengths = MysqL_fetch_lengths(pstRes); for(size_t i = 0; i < vtFIElds.size(); i++) { if(stRow[i]) { mpRow[vtFIElds[i]] = string(stRow[i],lengths[i]); } else { mpRow[vtFIElds[i]] = ""; } } data.data().push_back(mpRow); } MysqL_free_result(pstRes); return data;}size_t MysqLHelper::updateRecord(const string &stablename,const string &sCondition){ string ssql = buildUpdatesql(stablename,mpColumns,sCondition); execute(ssql); return MysqL_affected_rows(_pstMql);}size_t MysqLHelper::insertRecord(const string &stablename,const RECORD_DATA &mpColumns){ string ssql = buildInsertsql(stablename,mpColumns); execute(ssql); return MysqL_affected_rows(_pstMql);}size_t MysqLHelper::replaceRecord(const string &stablename,const RECORD_DATA &mpColumns){ string ssql = buildreplacesql(stablename,mpColumns); execute(ssql); return MysqL_affected_rows(_pstMql);}size_t MysqLHelper::deleteRecord(const string &stablename,const string &sCondition){ ostringstream ssql; ssql << "delete from " << stablename << " " << sCondition; execute(ssql.str()); return MysqL_affected_rows(_pstMql);}size_t MysqLHelper::getRecordCount(const string& stablename,const string &sCondition){ ostringstream ssql; ssql << "select count(*) as num from " << stablename << " " << sCondition; MysqLData data = queryRecord(ssql.str()); long n = atol(data[0]["num"].c_str()); return n;}size_t MysqLHelper::getsqlCount(const string &sCondition){ ostringstream ssql; ssql << "select count(*) as num " << sCondition; MysqLData data = queryRecord(ssql.str()); long n = atol(data[0]["num"].c_str()); return n;}int MysqLHelper::getMaxValue(const string& stablename,const string &sCondition){ ostringstream ssql; ssql << "select " << sFIEldname << " as f from " << stablename << " " << sCondition << " order by f desc limit 1"; MysqLData data = queryRecord(ssql.str()); int n = 0; if(data.size() == 0) { n = 0; } else { n = atol(data[0]["f"].c_str()); } return n;}bool MysqLHelper::existRecord(const string& sql){ return queryRecord(sql).size() > 0;}long MysqLHelper::lastInsertID(){ return MysqL_insert_ID(_pstMql);}size_t MysqLHelper::getAffectedRows(){ return MysqL_affected_rows(_pstMql);}//////////////////////////////////////////////////////////////////////////////////////////////////////////////MysqLHelper::MysqLRecord::MysqLRecord(const map<string,string> &record):_record(record){}const string& MysqLHelper::MysqLRecord::operator[](const string &s){ map<string,string>::const_iterator it = _record.find(s); if(it == _record.end()) { throw MysqLHelper_Exception("fIEld '" + s + "' not exists."); } return it->second;}//////////////////////////////////////////////////////////////////////////////////////////////////////////////vector<map<string,string> >& MysqLHelper::MysqLData::data(){ return _data;}size_t MysqLHelper::MysqLData::size(){ return _data.size();}MysqLHelper::MysqLRecord MysqLHelper::MysqLData::operator[](size_t i){ return MysqLRecord(_data[i]);}}//end namespace
3.3使用demo
/*****************************************************@brIEf:MysqLhelper demo*@autor:lvlv*@date:2016.06.12*@MysqL version:MysqL Community Server 5.6.30 (GPL)****************************************************/#include <string.h>#include <iostream>#include <string>using namespace std;#include "MysqLHelper.h"using namespace MysqLhelper;int main(int argc,char* argv[]){ //初始化MysqL对象并建立连接 MysqLHelper MysqLHelper; MysqLHelper.init("119.29.184.114","root","123456","StudentCourse"); try{ MysqLHelper.connect(); }catch(MysqLHelper_Exception& excep){ cout<<excep.errorInfo; return -1; } //增加一条学生记录 //示例插入语句 //string sql="insert into student values("201421031060","吕吕","华南理工大学","2014","软件工程",1)"; MysqLHelper::RECORD_DATA record; record.insert(make_pair("studentNo",make_pair(MysqLHelper::DB_STR,"201421031060"))); record.insert(make_pair("name","吕吕"))); record.insert(make_pair("school","广州中医药大学"))); record.insert(make_pair("grade","2014"))); record.insert(make_pair("major","计算机科学与技术"))); record.insert(make_pair("gender",make_pair(MysqLHelper::DB_INT,"1"))); int res=0; try{ res=MysqLHelper.insertRecord("student",record); }catch(MysqLHelper_Exception& excep){ cout<<excep.errorInfo; return -1; } cout<<"res:"<<res<<" insert successfully "<<endl; //删除一条学生记录,学号为201421031059 try{ res=MysqLHelper.deleteRecord("student","where studentNo=\"201421031059\""); }catch(MysqLHelper_Exception& excep){ cout<<excep.errorInfo; return -1; } cout<<"res:"<<res<<" delete successfully "<<endl; //查找学号为201421031059的学生选择的所有课程名称 MysqLHelper::MysqLData dataSet; string querysql="select coursename from course co where co.courseNo in (select courseNo from courseSelection where studentNo=\"201421031060\")"; try{ dataSet=MysqLHelper.queryRecord(querysql); }catch(MysqLHelper_Exception& excep){ cout<<excep.errorInfo; return -1; } cout<<"query successfully"<<endl; for(size_t i=0;i<dataSet.size();++i){ cout<<dataSet[i]["coursename"]<<endl; } //修改学号为201421031060的学生专业 MysqLHelper::RECORD_DATA recordChange; recordChange.insert(make_pair("major","软件工程"))); try{ res=MysqLHelper.updateRecord("student",recordChange,"where studentNo=\"201421031060\""); }catch(MysqLHelper_Exception& excep){ cout<<excep.errorInfo; return -1; } cout<<"res:"<<res<<" update successfully"<<endl; return 0;}
3.4makefile
################################### @brIEf:make scripts# @date:2016.05.28# @author:lvlv###################################environment varVPATH+=.CC:=g++FLAGS=-g -Wall -std=c++11INC+=-I/usr/local/MysqL/includeliBDIR+=-L/usr/local/MysqL/libCPPDirs=.CPPS=$(shell for dir in ${CPPDirs};do echo $${dir}/*.cpp;done)OBjdiR=objOBJs=$(patsubst %.cpp,${OBjdiR}/%.o,$(notdir ${CPPS}))TARGET:=MysqLDemo.out${TARGET}:${OBJs} ${CC} ${FLAGS} ${OBJs} -o $@ ${liBDIR} -lMysqLclIEnt${OBjdiR}/%.o:./%.cpp ${CC} ${FLAGS} ${INC} -o $@ -c $<.PHONY:cleanclean: rm -f ${TARGET} ${OBjdiR}/*
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
总结以上是内存溢出为你收集整理的C++利用MySQL API连接和 *** 作数据库实例详解全部内容,希望文章能够帮你解决C++利用MySQL API连接和 *** 作数据库实例详解所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)