一、vetcor
vector是C++ Standard Library提供的一种容器,具有自动增长内存、可随机存取的特性。简单的说可以像使用动态数组一样使用它。
二、错误
最近用MFC写一个基于SQLITE3数据库的信息记录程序。为了方便操作,将SQLITE3封装成了CSqlite3类并提供宽字符集操作函数。由于存取的信息均为字符串类型数据,于是在类CSqlite3中定义了一个结构来存储每次查询的结果集。如下结构:
typedef vector<wchar_t*> RecordValue;
typedef struct tagRecordSet{
RecordValue m_ColumNames;
RecordValue* <span style="white-space:pre"> </span>m_RecordValues;
unsigned m_nColumns;
unsigned m_nRows;
}RecordSet;
因为使用vector容器,所以不需要为内存的分配操心。通过sqlite3_exec成功查询后,sqlite3调用回调函数参数所指定的函数将数据存储到上述结构中。学习oracle数据库的时候也写过一个类似类,可存储多个字符串,自动增长,预留内存,随机存取。但是始终觉得用着别扭,所以在这次的编写中用了vector,以前也没有用过。在上述结构中,m_ColumnNames字段存储了结果集的字段信息,方便在CListCtrl中显示数据。在一开始的设计中,因为考虑到大部分查询结果集都拥有同样的字段信息,于是在UI类中设置了同样的字段来保存结果集字段信息,以减少为了获取结果集字段信息的操作。只需要在数据库中模仿ORACLE数据库建立几张信息表存储数据库中表信息,就可以通过这些表来获取某一张表的详细信息。将表的信息存储到UI类中,就不需要在查询具有同样字段的结果集时都进行字段信息的存储操作。于是开始这个结构并没有这个字段,出了错误才增加的。也许有人已经知道错误在那里。没错那就是因为没用过vector,我想当然的认为神圣的STL会在使用vetcor<wchar_t*>时,vector中存储的是字符串而不是字符串指针。
我以为它会自动为存入的字符串分配空间,将字符串拷贝进去,但实际上存储的只是指针而已。而因为sqlite3是用C语言写的,在使用MFC开发的时候,我都使用宽字符集。于是CSqlite3中有两个工具函数:ToMultiByte和ToWideChar,分别用于将字符串转换为多字节字符集和宽字节字符集。当然里面会有new操作并且在该函数内并不会有delete操作,所以在直到进行结果集清理前这new出来的字符串存储内存会一直存在。故在vector中存储指针除了不安全外也是可行的。
在完成基本的数据库操作功能前,没有任何问题产生。直到设计数据库管理界面时(这也是上述的建立系统表的用途),为了实现新建表的功能,使用CDialogEx类来获取用户的选择信息,并将其存储在vector<wchar_t*>中,再将其传出由操作界面来分析vectot<wchar_t*>中存储的信息。但是这里问题就出现了。在CDialogEx(派生类CTableInfo)中获取信息再到存储信息到vector的中间变量是局部变量。局部变量的声明周期仅到函数结束,也就是说,这个函数一旦返回,vector<wchar_t*>中存储的指针所指向的已经是垃圾数据。在我上面的想当然的想法下,我一直以为是因为CDialogEx基类中的某些操作导致了数据的变化。但是通过调试发现变量地址信息中的异常,到此我也意识到错误在哪里:临时变量被清理,数据无法传递出来。
要解决这个问题也简单,譬如使用vector<CString>就行了。用vetcor就是为了面对未知数量的字符串时,不用去考虑内存分配的问题。只是使用其他vector<Type>时需要一些转换操作,很麻烦。如CString如何转为PTSTR,网上有很多方法,但是我试验过的成功的并没有几个。而且有的对含有中文的字符串转换并不成功,大部分转换出来中文都是乱码。
写这篇文章也是为了提醒自己以后不要这样异想天开,不知道的东西就先测试它是否满足自己的需求。
类CSqlite3目前只是具有一些基本的数据库操作功能,这留下类的声明,在随着开发基于数据库的应用中慢慢将其完善。
#pragma once
extern "C"{
#include "R:/Archive/CodeLibrary/sqlite3/sqlite3.h"
}
//导入库
#pragma comment(lib,"R:/Archive/CodeLibrary/sqlite3/sqlite3.lib")
//使用vector存储结果
#include <vector>
using namespace std;
//自定义定义声明文件
#include "R:\Archive\CodeLibrary\Ghostdef.h"
//
typedef vector<wchar_t*> RecordValue;
class CSqlite3
{
private:
//database
sqlite3* m_db;
typedef struct tagRecordSet{
<span style="white-space:pre"> </span>
RecordValue m_ColumNames;<span style="white-space:pre"> </span>//字段
RecordValue* <span style="white-space:pre"> </span>m_RecordValues;<span style="white-space:pre"> </span>//信息
unsigned m_nColumns;<span style="white-space:pre"> </span>//字段数
unsigned m_nRows;<span style="white-space:pre"> </span>//行数
}RecordSet;
//结果集
RecordSet m_RecordSet;
//存储最近的SQL
vector<CString> m_LastQuery;
private:
//转多字节字符集
static char* ToMultiByte(wchar_t* pszStr);
//转宽字符集
static wchar_t* ToWideChar(char* pszStr);
//获取结果集的回调函数
static int Results(void* pParam, int nColumns, char** pszColumnValue, char** pszColumnName);
//清理结果集
void Clean();
public:
//构造函数
CSqlite3();
//拷贝构造函数
CSqlite3(CSqlite3& sqlite3);
//析构函数
~CSqlite3();
//打开
bool Open(char* pstrDatabase);
bool Open(wchar_t* pstrDatabase);
//查询
GHOST Query(char* pstrSql);
GHOST Query(wchar_t* pstrSql);
//插入
GHOST Insert(char* pstrSql);
GHOST Insert(wchar_t* pstrSql);
//更新
GHOST Update(char* pstrSql);
GHOST Update(wchar_t* pstrSql);
//删除
GHOST Delete(char* pstrSql);
GHOST Delete(wchar_t* pstrSql);
//执行SQL语句
GHOST Execute(char* pstrSql);
GHOST Execute(wchar_t* pstrSql);
//关闭数据库
void Close();
/****操作结果****/
//获取结果集字段数
unsigned GetColumns()const;
//获取结果集行数
unsigned GetRowCount()const;
//获取字段信息
RecordValue GetColumnNames()const;
//获取某一结果
wchar_t* GetRecordSetItem(unsigned Row,unsigned Col = 0)const;
//获取结果集的信息数据部分
RecordValue GetRecordSetValue(unsigned Col = 0)const;
//tools
//是否为空结果集
bool IsEmpty()const;
//执行最近的上一次查询(使用SaveQuery来存储)
bool ExcuteLastQuery();
//保存最近的SQL语句(还没考虑好实在执行SQL语句时由操作函数自己保存,
//还是由外部手动选择保存哪些SQL语句)
void SaveQuery(CString strSql);
};
这个类中现在数据库操作函数的参数均为一个参数,SQL语句,后面慢慢改成参数指定信息,把SQL语句的合成转移到函数内部。否则只需要一个Execute函数就够了。