string类是C++中用来处理字符串的极其方便的工具,不同于C语言中的字符串处理函数,string类可以把字符串当成普通的基本数据类型处理,可以灵活地采用赋值“=”,大小判断“< > ==”,还有加法运算“+ +=”等等,而且不用像字符数组一样预设空间大小,总之可以说是兼顾了字符数组的优点,有弥补了C风格的字符串处理起来不方便的地方。
今天我们就来自己尝试一下对C语言中原有的字符串处理函数进行包装,自定义一个String类出来。
首先是对String类进行一个总体的声明
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
class String
{
public:
String(const char *str = NULL);
~String();
String(const String &x);
int length()const
{
return strlen(sdata);
}
String substr(int pos = 0, int len = -1)const;
int find(const char *str, int pos = 0)const;
int find(const String &x, int pos = 0)const;
int find(char ch, int pos = 0)const;
int rfind(const char *str, int pos = 0)const;
int rfind(const String &x, int pos = 0)const;
int rfind(char ch, int pos = 0)const;
const char *c_str()const;
String operator = (const char *str);
String operator = (const String &x);
String operator + (const char *str);
String operator + (const String &x);
String operator += (const String &x);
String operator += (const char *str);
char& operator [] (unsigned int x);
bool operator == (const char *str)const;
bool operator == (const String &x)const;
friend ostream& operator << (ostream &out, const String &x);
friend istream& operator >> (istream &in, String &x);
private:
char *sdata;
};
如果是在工程文件里,那么以上的部分应该放在String.h中,这里只是实现了一些string类较常用的功能,用来给大家展示一下C++语言的优美之处,下面给出各个成员函数的实现
//构造函数
String::String(const char *str)
{
if (!str)
{
sdata = new char[1];
sdata[0] = '\0';
}
else
{
int len = strlen(str);
sdata = new char[len + 1];
strcpy(sdata, str);
}
}
String::~String()//析构函数
{
delete[]sdata;
}
String::String(const String &x)//复制构造函数
{
int len = strlen(x.sdata);
sdata = new char[len + 1];
strcpy(sdata, x.sdata);
}
String String::substr(int pos, int len)const//提取子串函数
{
if (len < 0)
len = strlen(sdata) - pos;
String x;
x.sdata = new char[len + 1];
strncpy(x.sdata, sdata + pos, len);//strncpy不自动添加'\0'
x.sdata[len] = '\0';
return x;
}
//查找函数的三种形式
int String::find(const String &x, int pos)const
{
return find(x.sdata, pos);
}
int String::find(const char *str, int pos)const
{
char *cp = strstr(sdata + pos, str);//查找子串函数
if (cp)
return cp - sdata;
else
return -1;
}
int String::find(char ch, int pos)const
{
char *cp = strchr(sdata + pos, ch);//查找字符函数
if (cp)
return cp - sdata;
else
return -1;
}
//反向查找的三种形式,返回目标串最后一次出现的位置
int String::rfind(const char *str, int pos)const
{
int k = -1;
int p = find(str, pos);
while (p >= 0)
{
k = p;
p += strlen(str);
p = find(str, p);
}
return k;
}
int String::rfind(const String &x, int pos)const
{
return rfind(x.sdata, pos);
}
int String::rfind(char ch, int pos)const
{
char *cp = strrchr(sdata + pos, ch);//字符反向查找函数
if (cp)
return cp - sdata;
else
return -1;
}
const char* String::c_str()const//返回c格式字符串
{
return sdata;
}
//赋值函数的两种形式
String String::operator = (const String &x)
{
*this = x.sdata;
return *this;
}
String String::operator = (const char *str)
{
if (strlen(sdata) >= strlen(str))//检查原空间是否足够
strcpy(sdata, str);
else
{
delete[]sdata;//原空间不足则需另外开辟空间
sdata = new char[strlen(str) + 1];
strcpy(sdata, str);
}
return *this;
}
//加法运算的两种形式
String String::operator + (const String &x)
{
return *this + x.sdata;
}
String String::operator + (const char *str)
{
String re;
delete[]re.sdata;
char *p = new char[strlen(sdata) + strlen(str) + 1];
p[0] = '\0';
strcat(p, sdata);
strcat(p, str);
re.sdata = p;
return re;
}
//加等于运算得两种形式
String String::operator += (const String &x)
{
return *this = *this + x;
}
String String::operator += (const char *str)
{
return *this = *this + str;
}
char& String::operator [] (unsigned int x)//取字符
{
int len = strlen(sdata);
if (x < len) return sdata[x];
return sdata[len - 1];
}
bool String::operator == (const char *str)const//相等
{
return strcmp(sdata, str) == 0;
}
bool String::operator == (const String &x)const
{
return strcmp(sdata, x.sdata) == 0;
}
ostream& operator << (ostream &out, const String &x)//输出流
{
out << x.sdata;
return out;
}
istream& operator >> (istream &in, String &x)//输入流
{
char ch[100];
in >> ch;
x = ch;
return in;
}
大部分函数的实现都是应用C语言中的字符串处理函数,由此可见String类的方便还是离不开底层C语言的支持(虽然真正的string类应该不会像我一样直接偷懒用C风格的字符串处理函数,但底层的原理大概是一样的),打个比方,String类就像是给原来粗糙的电路板罩上了一个漂亮的外壳,变成了一个对用户更加友好的产品。
接下来没什么好说的,代码实现并不是很难,但我要提示的一点是,有时候我们可以利用之前定义的函数让代码更加简洁,例如find和rfind的实现
int String::find(const String &x, int pos)const
{
return find(x.sdata, pos);
}
int String::rfind(const String &x, int pos)const
{
return rfind(x.sdata, pos);
}
还有就是‘\0’,因为系统在处理字符串是以‘\0’作为结束标志的,如果处置不当把'\0'给丢了,很容易造成数组访问越界等一系列问题,我在实现对'+'运算符进行重载时就在'\0'上出了错
//加法运算的两种形式
String String::operator + (const String &x)
{
return *this + x.sdata;
}
String String::operator + (const char *str)
{
String re;
delete[]re.sdata;
char *p = new char[strlen(sdata) + strlen(str) + 1];
p[0] = '\0';//系统在执行strcat时会去找p中的'\0'作为p字符串的结尾,而如果找不到的话,系统就会一直向后遍历
strcat(p, sdata);//直到找到一个内存单元内容为'\0',这样就会不知不觉地造成访问越界的问题
strcat(p, str);
re.sdata = p;
return re;
}