前言:
从进入IT行业以来,也是由一个新手慢慢入门到熟悉一门编程语言的过程。在这些时间以来,也接触过很多的开发人员,也带过一些开发团队,每个人在编程风格和某种技术的使用上都有很大的不同。我见到过有些研究生在毕业课题中将模板、智能指针等用得出神入化的,也见到过公司的软件高手用数组实现大规模的数值计算和软件模拟的。而不大注重程序的机构,逻辑的严谨,害怕使用指针等是很多C++入门程序员的一个显著的特征。而在VC++实际开发过程中,模板和单键实例的结合开发是一个非常常见的过程,掌握好这些方法对规范程序开发,提高程序可维护性和可读性都是有很好的帮助,希望我们的程序员能够在自己开发的应用程序中多使用这些功能。
正文:
模板和单键实例等原理和应用在网络上都有很多的介绍,这里不做详细的使用介绍,只做简单的几个要使用到的接口进行说明。常见的模板有vector,map,multimap,set等,而我们实际使用最多的就是vector和map,在熟练的程序员手中,模板嵌套模板等是经常使用的方法。
Vector是最常使用的模板,我们可以把他看成是一个数组,一个能够自己管理数组长度的数组,不过在线程安全和内存释放等方面需要自己进行处理。常用的使用接口有添加元素、删除元素和遍历元素。添加元素使用的接口是:push_back,删除元素用:erase,遍历元素一般分两种方法,顺序遍历和迭代器的方法。大致使用方法如下:
模板的使用需要包含相应的头文件和使用std命名空间,一般在程序的前面包含模板文件。
#include <vector>
#include <map>
Using namespace std;
以整形数据为例,定义模板
vector<int> vecIntList;
添加元素:
Int nVal = 1234;
vecIntList.push_back(nVal);
遍历元素:方法一:
For (int i=0; i<vecIntList.size(); i++)
{
vecIntList.at(i);
……………………..
}
方法二:
Vector<int>::iterator iter_IntList;
For (iter_IntList=vecIntList.begin(); iter_IntList!=vecIntList.end(); iter_IntList++)
{
*iterIntList;
………………………
}
删除元素:我们可以采用迭代器遍历元素的方法找到要删除的元素,然后使用erase接口就可以实现了。
Map模板的使用类似vector模板,不过多了一个常用的接口,那就是find,可以根据对应关系定位到查询的数据位置,然后根据first和second实现对map数据的访问,这里就不多作叙述。
单键实例也是程序最常用的一种使用方法,他的主要作用是在系统中实现对象的唯一实例化。在很多时候可以作为数据的管理。使用方法是:在我们定义我们的自己类的时候将系统自动生成的类的构造函数和析构函数修改成为保护成员或者私有成员,这样就不可以通过A a进行对象的实例化,这个时候提供一个静态实例化对对象的方法,通过静态变量的特点实现对象的实例化。
单键实例和模板的结合编程可以实现系统的模块化管理。比如在很多程序员关于简单的学生信息编程的时候都是在需要调用这些信息数据的时候进行数据库的直接读写操作,所以当我们阅读他们的程序的时候,就会发现到处都是数据库的SELECT,UPDATE等操作。这样显得程序的逻辑非常混乱,如果是多人来实现这个工程的工作导致维护非常不方便。这个时候我们可以用单键实例的方法,将这些数据封装成一个数据管理中心。我们可以将程序分为数据管理中心、数据管理中心和数据库接口、前台界面和数据库管理中心接口等部分,假如有不同的人来完成各个部分的编写,这样效率也会提高,不会出现因为工程的数据模型发生变化的时候到处找程序进行信息修改,只需要在相应的部分中修改就能够完成信息的同步。比如界面人员就可以更关注界面而不用考虑数据管理中心的变化。
这里以一个简单的学生信息管理来模拟这些的使用。学生信息管理就用简单的两个实体表,一个学生基本信息,一个课程信息,学生和课程信息为一对多,这里用到的模板是vector和map两种。
学生基本信息:编号、性别、姓名、手机号码、家庭住址
课程信息:课程编号、课程名、老师名
然后根据这个信息进行结构体的抽象。在程序编写过程中,合理使用结构体也是非常重要的,我记得很多有经验的程序员经常说看一个人的水平的好坏看他对结构体的使用就能够知道。比如可以这样抽象这些信息:(每个人有不同的使用方法,你也可以使用别的方法,我这里做参考)
首先通过VC向导建立一个类,实现数据管理中心类的编写。
抽象性别,因为是一种选其一的情况,这里用枚举类型进行描述。
//定义性别
typedef enum _Sex
{
MALE = 0,
FEMALE = 1
}SEX;
然后就是学生基本信息和课程信息的结构体描述了。这里需要在定义的时候进行明确一些对象的使用,是将这些数据定义成结构体还是类,一般的在以某种行为为主的方法中,一般用类,比如程序的数据检查,复杂的运算符重载和线程安全等,以数据中心就用结构体来实现。而在抽象这些对象的时候,重载某些运算符也是常见的方法,这里重载==运算符,在程序校验数据过程中,常用编号进行校验,所以这里重载的方法用id进行重复性判断
//-----------------------结构体定义
//学生基本信息
typedef struct _StudentInfo
{
int nID; //编号
SEX enumSex; //性别
char strName[10]; //姓名
char strAddress[MAXMSGLENGTH + 1]; //住址
_StudentInfo()
{
nID = -1;
enumSex = MALE;
memset(strName, 0, sizeof(strName));
memset(strAddress, 0, sizeof(strAddress));
}
//重载一个比较的方法,这里用编号做判断条件
bool operator == (int nID)
{
return this->nID == nID ? true : false;
}
}STUDENTINFO;
//课程
typedef struct _CourseInfo
{
int nID; //课程编号
char strName[10]; //课程名称
_CourseInfo()
{
nID = -1;
memset(strName, 0, sizeof(strName));
}
//重载一个比较的方法,这里用编号做判断条件
bool operator == (int nID)
{
return this->nID == nID ? true : false;
}
}COURSEINFO;
基本数据类型抽象完成,下面可以实现系统的编程了,首先进行单键实例方法的编写。
定义对象:
private:
static CStudentMgr * m_Instance;
实现对象的实例化:
CStudentMgr * CStudentMgr::GetInstance()
{
if (m_Instance == NULL)
{
m_Instance = new CStudentMgr();
}
return (CStudentMgr *)m_Instance;
}
然后用模板在实例对象中进行数据的抽象,由于模板在程序中经常使用,遍历的方法这里使用迭代器的方法,所以这里对这些进行简单的定义。在模板的使用过程中,一般建议多使用指针,因为大家都知道,指针的效率是最高的。
//定义STL模板
typedef vector<STUDENTINFO *>::iterator ITER_VEC_STUDENTINFO;
typedef vector<STUDENTINFO *> VEC_STUDENTINFO;
typedef vector<COURSEINFO *>::iterator ITER_VEC_COURSEINFO;
typedef vector<COURSEINFO *> VEC_COURSEINFO;
typedef vector<int>::iterator ITER_VEC_COURSEID;
typedef vector<int> VEC_COURSEID;
typedef map<int, VEC_COURSEID *>::iterator ITER_MAP_STUDENT_COURSE;
typedef map<int, VEC_COURSEID *>
private:
static CStudentMgr * m_Instance;
VEC_STUDENTINFO m_vecStudentInfo;
VEC_COURSEINFO m_vecCourseInfo;
MAP_STUDENT_COURSE m_mapStudentCourse;
由于vector模板在释放的过程中不会自动释放内存,所以在模板使用指针动态申请内存在删除的时候需要手动遍历模板进行各个元素的内存的释放,这里需要注意。
准备工作完成,下面可以进行管理中心类的接口编写了。
public:
//初始化
bool InitItemInfo();
//维护student
bool AddStudent(const int nID, const SEX enumSex, const char * strName, const char * strAddress);
bool ModifyStudent(const int nID, const SEX enumSex, const char * strName, const char * strAddress);
bool DeleteStudent(const int nID);
STUDENTINFO * GetStudent(const int nID);
VEC_STUDENTINFO * GetStudentList();
//维护course
bool AddCourse(const int nID, const char * strName);
bool ModifyCourse(const int nID, const char * strName);
bool DeleteCourse(const int nID);
COURSEINFO * GetCourse(const int nID);
由于在使用过程中需要使得数据和数据库同步,这里在数据操作的时候需要与数据库进行接口。由于这个管理中心类向界面进行提供接口,他不需要知道如何与数据库接口,所以这里与数据库同步可以用私有的方法来实现。
protected:
//初始化保护方法
bool ReadStudentInfo();
bool ReadCourseInfo();
bool ReadStudentCourseMap();
void AddStudentCourseMap(const int nStudentID, const int nCourseID);
//student 保护方法
bool IsExistStudentByID(const int nID);
bool AddStudentToDB(const int nID, const SEX enumSex, const char * strName, const char * strAddress);
bool ModifyStudentFromDB(const int nID, const SEX enumSex, const char * strName, const char * strAddress);
bool DeleteStudentFromDB(const int nID);
bool DeleteStudentFromMemory(const int nID);
//course 保护方法
bool IsExistCourseByID(const int nID);
bool AddCourseToDB(const int nID, const char * strName);
bool ModifyCourseFromDB(const int nID, const char * strName);
bool DeleteCourseFromDB(const int nID);
bool DeleteCourseFromMemory(const int nID);
然后就是各个方法的具体实现了。实现的原理大致就是实现模板数据的维护,具体的方法可以看看这里的学生基本信息的实现。别的诸如类推。
bool CStudentMgr::AddStudent(const int nID, const SEX enumSex,
const char * strName, const char * strAddress)
{
//首先判断是否存在ID则不允许添加
if (IsExistStudentByID(nID))
{
return false;
}
//首先添加到数据库再添加到内存
if (AddStudentToDB(nID, enumSex, strName, strAddress))
{
STUDENTINFO *pInfo = new STUDENTINFO;
pInfo->nID = nID;
pInfo->enumSex = enumSex;
strcpy(pInfo->strName, strName);
strcpy(pInfo->strAddress, strAddress);
m_vecStudentInfo.push_back(pInfo);
}
return true;
}
bool CStudentMgr::AddStudentToDB(const int nID, const SEX enumSex,
const char * strName, const char * strAddress)
{
char strSql[1024];
memset(strSql, 0, sizeof(strSql));
sprintf(strSql, "insert into student(student_id, sex, name, address) values (%d, %d, '%s', '%s')",
nID, (int)enumSex, strName, strAddress);
if (!CAdoDispose::GetInstance()->ExecuteSQL(strSql))
{
return false;
}
return true;
}
bool CStudentMgr::IsExistStudentByID(const int nID)
{
ITER_VEC_STUDENTINFO iter_Student;
for (iter_Student=m_vecStudentInfo.begin(); iter_Student!=m_vecStudentInfo.end(); iter_Student++)
{
if (*(*iter_Student) == nID)
{
return true;
}
}
return false;
}
其他的详细实现方法见程序源代码的实现。与数据库的接口这里封装一个ADO操作类,具体也可以见源代码实现。前台界面调用很简单,通过实例化指针调用就可以了,比如:
void CStudentManagerView::OnAdd()
{
UpdateData(TRUE);
if (!CStudentMgr::GetInstance()->AddStudent(m_nID, (SEX)m_nSex, m_strName.GetBuffer(0), m_strAddress.GetBuffer(0)))
{
MessageBox("添加学生信息失败,请检查!", "错误", MB_OK | MB_ICONERROR);
}
}
void CStudentManagerView::OnModify()
{
UpdateData(TRUE);
if (!CStudentMgr::GetInstance()->ModifyStudent(m_nID, (SEX)m_nSex, m_strName.GetBuffer(0), m_strAddress.GetBuffer(0)))
{
MessageBox("修改学生信息失败,请检查!", "错误", MB_OK | MB_ICONERROR);
}
}
void CStudentManagerView::OnDelete()
{
UpdateData(TRUE);
if (!CStudentMgr::GetInstance()->DeleteStudent(m_nID))
{
MessageBox("删除学生信息失败,请检查!", "错误", MB_OK | MB_ICONERROR);
}
}
上面用实例的方法使用模板和单键实例方法实现了程序的编写,这样能够使程序更加严谨,更易于维护。如果是大容量的数据,我们可以不用将程序数据全部加载到内存中间,可以选择的加载。因为内存的效率和速度始终是最高的,不过这里要维护好内存和数据库的同步。如果您在程序开发中有什么好的使用技术和方法等,欢迎您和我交流:
EMAIL:successq_g@163.com
QQ:5516853
欢迎访问最专业的网吧论坛,无盘论坛,网吧经营,网咖管理,网吧专业论坛
https://bbs.txwb.com
关注天下网吧微信/下载天下网吧APP/天下网吧小程序,一起来超精彩
|
本文来源:vczx 作者:佚名