3.vector实现字符串类
本章前言:
身为土生土长的中国程序员,你肯定要用unicode来编写程序。但是由wchar_t数组实现的字符串准确点说应该叫字符数组,但它在使用时比较繁琐,而且容易出现数组越界和字符串结尾不为0的错误。 为了方便实现字符串相关的功能,DND引擎抽象了String类,其底层是vector<wchar_t>。为什么不使用std::string呢,因为其不能定制化功能,例如+操作符直接连接两个字符串、使用sprintf格式化生成字符串等等。
目标要点总结:
1. 实现字符串类
2. 类似基础类型的使用方式
最终效果:
例如下面这样使用:
Stringstr=String(L”Hello”)+ L” ” + L”DND!”;//str等于Hello DND!
具体的接口可以查看头文件中的注释。
class StrVector;
class DLL_APIString
{
public:
//==================构造、析构、操作符========================
String();//空串
String(constchar*str);//字符数组
String(constwchar_t*wcs);//宽字符数组
String(wchar_twc);//宽字符
~String();//析构
String(constString&b);//复制构造
//String(const unsigned b);
String(constintb);//数字构造
String(wchar_tch,unsigned len);//填充len个 ch
String(StrVector*);//strvector构造
String& operator=(constString&b);//=号重载
String operator+(constString&b) const;//连接
bool operator==(constString&b) const;//相同
bool operator<(constString&b) const;//小于
unsigned Get_Length() const;//返回长度
//==================转化==================
const wchar_t*Get_Wcs()const;
void Get_Wide_Char_Str(wchar_t* target, unsigned max_len) const;//获得宽字符数组
void Get_Multi_Byte_Str(char* target, unsigned max_len) const;//获取字符数组
int Get_Int();//返回int
void Clear();//设为空串
void Pop();//去掉结尾
//==================查找==================
unsigned Find_End(wchar_t wc);//查找最后一个
unsigned Find_Str(const String&str);//查找字符串位置,返回-1代表不存在
unsigned Find_N(wchar_t wc,unsignedN);//查找第n个要查找的字符
unsigned Get_Char_Count(wchar_t ch);//返回某字符出现的个数
String Get_Str(unsigned begin, unsigned end);//返回区间内的字符串
//==================删除==================
void Cut(unsigned begin,unsignedend);//去掉区间内的字符串 [b, e]
void Cut_Tail(unsigned i);//去掉i位置后的包括i
void Cut_Head(unsigned i);//去掉i位置前的包括i
void Cut_Head_Str(const String& str);//去除头部字符串
//==================修改==================
void Delete_Char(unsigned i);//i位置删除一个字符
void Insert_Char(unsigned i, wchar_t ch);//i位置前插入一个字符,第一个字符位置为0
void Replace_Char(wchar_t source, wchar_t target);//替换某个字符
unsigned Split(wchar_t wc,String*strs, unsigned max_size);//返回实际分隔后的字符串个数。例如 a;b;返回 a b 2
static StringFormat(unsignedmax_size,const wchar_t* format, ...);//max size不包含结束符
private:
StrVector* p;
void _init();
void _copy(const wchar_t*wcs);
};
前题简要:
无。
具体实现:
由于需要生成动态链接库,所以要在导出的接口中隐藏掉vector<wchar_t>类,可以使用void指针保存指向实际的vector<wchar_t>对象的地址,然后在接口定义中进行指针强转。但这样代码看上去过于杂乱,有更好的方式实现。在前面的头文件可以看到StrVector类的声明:
class StrVector;
这样就能在String类中声明StrVector指针类型的变量p。在cpp中才实现了StrVector的定义,StrVector的定义如下:
class StrVector :publicvector<wchar_t>
{
public:
StrVector(vector<wchar_t>*p) :
vector<wchar_t>(*p){}
StrVector(){}
};
这样String类的指针p就能直接访问基类vector<wchar_t>的成员。p指针的初始化交给String类的构造函数,构造函数会调用_init()。
void String::_init()
{
p = newStrVector;
}
例如默认构造函数会构造一个空串,如下:
String::String()
{
_init();
}
使用char字符数组构造:
String::String(constchar*str)
{
_init();//初始化
unsigned len =strlen(str) + 1;//字符串长度
wchar_t* wcs =new wchar_t[len];//分配临时内存
//multi_byte转wide_char(即char数组转 wchar_t)
MultiByteToWideChar(CP_ACP,NULL,str, -1, wcs, len);
//设置vector最大长度为len
p->reserve(len);
//复制字符串
_copy(wcs);
delete[] wcs;
}
其中首先获取字符串长度,然后分配足够的内存大小(加1放下结束符)。然后调用MultiByteToWideChar函数将char数组转换至wchar_t数组,再预先设置vector<wchar_t>的最大长度。调用_copy将数据从buffer复制到vector,最后释放buffer。
连接字符串的实现如下,首先复制构造一个临时对象str,然后在尾部插入b字符的所有字符,最后再返回拷贝:
String String::operator+(constString&b) const
{
String str = *this;
str.p->insert(str.p->end(),b.p->begin(),b.p->end());
return str;
}
有很多系统函数、库函数都要求以const wchar_t*类型作为函数参数,让String快速的返回字符串首地址很有必要。由于String的vector不包含结束符,所以需要扩容尾部为0,但长度仍然不变。实际的实现如下:
const wchar_t*String::Get_Wcs()const
{
p->push_back(0);
wchar_t * temp = &(*p)[0];//保证0下标不越界
p->pop_back();
return temp;
}
比如在排序两个字符串的时候就用到了Get_Wcs,如下:
bool String::operator<(constString&b) const
{
return wcscmp(Get_Wcs(),b.Get_Wcs()) < 0;
}
sprintf函数用来格式化字符串非常好用,所以有必要封装类似的接口,封装之后可以这样生成一个字符串:
String str =String::Format(256,L"FPS:%d dt:%f",fps, dt);
Format是一个静态变长参数函数,使用方式类似于sprintf,第一个256是指临时buffer的最大长度,具体实现如下:
String String::Format(unsignedmax_size,const wchar_t* format, ...)
{
wchar_t* wcs =new wchar_t[max_size + 1];
va_list args;
va_start(args,format);
vswprintf_s(wcs,max_size + 1,format,args);
va_end(args);
String ret =String(wcs);
delete[] wcs;
return ret;
}
有关查找、删除、编辑功能的实现这里就不贴了,很简单,使用vector的接口很好实现。作者以前实现的String是基于wchar_t数组的,由自己慢慢的添加接口而成(手动写每个算法真的很麻烦)。最近规范下了命名,并换用了vector<wchar_t>实现,一天就完成了,所以标准库还是好用啊!
作者:略游
日期:17-06-20
QQ:1339484752