一个纯C++简陋字符串实现说明,之前是发的原代码,结果太长。无法让大家看清。不好意思啊。
其实中的设计很简单,首先是数据成员设计,只有一个指针。也就是sizeof()它的时候,为4(32位系统下),而STL的string的sizeof()是32。默认情况下,是不会配置内存的,相当于定义了一个空指针。这个指针类型是T *。之所以使用T *,是因为这样调试的时候,可以看到该字符串内容是什么。如果你定义成void *,那就什么也看不到了。
在XStringBase定义如下:
T * m_Data; /**////<数据指针
一但它有数据后,它就指向字符串描述结构的字符串内容部分。在这个字符串前面,还有8个字节的字符串头。结构体定义如下
///字符串结构结构体
struct SStringStruct
{
XInt Length; ///<尺寸
XInt Capacity; ///<当前字符串的容量
//Data ///<当前数据指针所在的位置
};
有两个内容,一个是当前字符串的长度。不含结尾0,一个是当前申请内存的内存大小:容量。
通过下面的函数,取得字符定义的结构体:
SStringStruct * getOriData()
{
return ((SStringStruct *)m_Data) - 1;
}
这样getOriData()->Length就是字符串的长度了,getOriData()->Capacity就是它的容量了。
///取当前容量
XInt getCapacity() const
{
XInt iRet = 0;
if( isNotNULL(m_Data) )
{
iRet = getOriData()->Capacity;
}
return iRet;
}
///取当前字符串的长度
XInt getLength() const
{
XInt iRet = 0;
if( isNotNULL(m_Data) )
{
iRet = getOriData()->Length;
}
return iRet;
}
这个字符串没有考虑内存节省的方式而使用COW(改变的时候写),我个人觉得没有必要,这样做会引入线程安全性的问题。我用的每个字符串对象都有一个拷贝。图省事吧,大多数的时候,不用考虑线程安全性的问题。
为了减少内存碎片,这里内存申请的大都是指定大的倍数。下面是定义:STRING_BLOCK_SIZE = 64;
//字符串内的常量定义,不给外部使用的
enum
{
STRING_BLOCK_SIZE = 64, //单位块大小
CHAR_SIZE = sizeof(T), //每个字符串的大小
HEAD_SIZE = sizeof(SStringStruct), //字符串头的字节数
XSTRING_BASE_ENUM_FORCE_DWORD = 0x7FFFFFFF //强制该Enum为32位
}; //字符串最小内存块大小
在字符的长度增加的时候,会使用ensureCapacity这个函数,确定当前容量是否够。如果不够将调用expandCapacity扩展所需要的容量。每次扩展的容量,默认是原来容量的2倍增长。
///确定装载字符容量(会自动增加0结尾)
void ensureCapacity(XInt paramCharCapacity)
{
if( paramCharCapacity > 0)
{
expandCapacity(paramCharCapacity + 1); //增加一个字符0的位置
}
}
///扩展容量
/**
注意:这个函数,并不会做安全检查
@param [in] paramMinimumCapacity 指定的最小容量,这个容量是字符个数
*/
template<class T,class Alloctor>
void XStringBase<T,Alloctor>::expandCapacity(XInt paramMinimumCapacity)
ZDH_THROW(XEOutOfMemory)
{
//ZDH_ASSERT(paramMinimumCapacity>0);
XInt iNowCapacity = getCapacity();
if( iNowCapacity < paramMinimumCapacity)
{
XInt iNewCapacity = paramMinimumCapacity * CHAR_SIZE + HEAD_SIZE; //取得实际所需的字节数
iNowCapacity *= 2;
if( iNewCapacity < iNowCapacity) iNewCapacity = iNowCapacity;
XInt iMod = iNewCapacity % STRING_BLOCK_SIZE;
//确保申请的内存为指定大小的倍数
if( iMod > 0 )
{
iNewCapacity += (STRING_BLOCK_SIZE - iMod);
}
SStringStruct * pData = (SStringStruct *)Alloctor::Alloc(iNewCapacity);
//检查内存是否溢出
if( pData == NULL )
{
throw XEOutOfMemory();
}
//设置基本属性
pData->Capacity = (iNewCapacity - HEAD_SIZE) / CHAR_SIZE;
pData->Length = getLength();
if( pData->Length > 0 ) //复制数据
{
CopyData( (T *)m_Data, (T *)(pData + 1), getLength() );
}
else
{
*((T *)(pData + 1)) = 0;
}
//释放原来的
if( m_Data != NULL )
{
Alloctor::Free(getOriData());
}
//开始替换
m_Data = (T *)(pData+1);
}
}
这个字符串我喜欢的地方是它提供了整数转字符串和字符串转整数的方法。还提供了类似printf和cat_printf格式字符串的函数。相信写过C和C++的朋友,应该都喜欢用吧。用它我就不用再像C一样考虑要给他临时分配多少空间。另外还提供了查找函数Pos,去除空格函数Trim,大小写转换函数uppercase,lowercase,替换ReplaceString,子串SubString等函数。这个字符串还重载了,[]<<等运符串,我们可以这样定义字符串:
XAnsiString strTemp;
strTemp <<"hello,我是","Rex","我今年",18,"岁";
也可以strTemp.printf("hello %d",18);
总之,就是为了字符串方便简单易用。
最后,在这里,内存分配使用的是new和delete,不存在移植的问题。