stl中String类的实现
Posted 大忽悠爱忽悠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了stl中String类的实现相关的知识,希望对你有一定的参考价值。
代码中写了详细的注释,这里就不展开对每个函数做说明解释了
string.h
#pragma once
#include<cstdlib>
#include<iostream>
using namespace std;
class String
{
private:
char* str;//指向动态数组的指针
int size;
void Error(const char* c)const;//错误信息报告
public:
//构造函数和析构函数
String(const char* c = "");//转换构造函数同时也是默认无参构造函数
String(const String& s);//复制构造函数
~String();
//访问方法:
//赋值运算符重载
String& operator=(const char* c);//转换赋值:类串=c串
String& operator=(const String& s);//转换赋值:类串=类串
//指定位置插入
String& Insert(int id, const String& s);//子串插入,插入到id下标之前
//指定位置,删除指定长度的子串
String& Erase(int id, int num);
//获取指定位置,指定长度的子串
String SubStr(int id, int num)const;
//串连接
String operator+(const String& s)const;//串连接:类串=类串+类串
String operator+(const char* c)const;//串连接:类串=类串+c串
friend String operator+(const char* c,const String& s);//串连接:类串=c串+类串
//串比较
bool operator==(const String& s);//串比较: 类串==类串
bool operator==(const char* c);//串比较:类串==c串
friend bool operator==(const char* c, const String& s);//串比较:c串==类串
//成员转换:特殊的operator类型转换函数
operator char* ()const; //将当前String对象转换为char*类型的对象,然后返回,即返回一个转换后的char* 对象
//下标运算符重载
char& operator[](int id); //非常量型下标运算符重载
const char& operator[](int id)const;//常量型下标运算符重载,注意这里的后置const不能少,因为只有后置const才能作为重载条件
//求串长
int Size(void)const { return size; }
//字符查找
int Find_First_Of(char ch, int id)const;
//子串查找
int Find_First_Of(const String& s, int id)const;
//输入输出运算符重载---做友元函数,因为左值要是输入输出符,而不是当前类对象
friend istream& operator>>(istream& istr, String& s);
friend ostream& operator<<(ostream& ostr, const String& s);
//串读取
int ReadString(istream& istr=cin, char delimiter='\\n');
};
string.cpp
#define _CRT_SECURE_NO_WARNINGS //如果关于strcpy等等函数报安全隐患,就加上这行代码,默认其为安全,注意:必须要置顶
#include"string.h"
#include<string.h>
//1.默认构造函数的实现--同时也是转换构造函数
//注意:如果声明的时候写了默认实参,那么实现的时候就不能再次写一遍,不然会报错
String::String(const char* c)
{
size = strlen(c);//strlen不包含\\0长度,sizeof包含\\0
str = new char[size + 1];//str在动态创建时的长度为size+1,是因为要包含一个\\0
if (!str)
Error("String: overflow!");
strcpy(str, c);
}
//2.错误信息报告
void String::Error(const char* c)const
{
cout << c << endl;
}
//3.赋值构造函数
String::String(const String& s)
{
size = s.size;
str = new char[size + 1];
if (!str)
Error("String: overflow!");
strcpy(str, s.str);
}
//4.成员赋值运算符重载
String& String::operator=(const char* c)
{
int len = strlen(c);
char* buf = str;
//如果长度一样,就不用重新开辟空间存储了
if (size != len)
{
str = new char[len + 1];
if(!str)
Error("String: overflow!");
delete[] buf;
size = len;
}
strcpy(str, c);
return *this;
}
String& String::operator=(const String& s)
{
if (size != s.size)
{
str = new char[s.size + 1];
if (!str)
Error("String: overflow!");
size = s.size;
}
strcpy(str, s);
return *this;
}
//5.成员转换函数---把当前string对象,转换为char*对象,并返回
String::operator char* ()const
{
char* c = new char[size + 1];
if(c==NULL)
Error("String: overflow!");
strcpy(c, str);
return c;
}
//6.串连接
//(1)类串与类串连接
String String::operator+(const String& s)const
{
String w;
int len = size + s.size;
delete[] w.str;
w.str = new char[len + 1];
if(!w.str)
Error("String: overflow!");
//一开始w是空的用拷贝,后来用连接字符串函数
strcpy(w.str, s.str);
strcat(w.str, str);
w.size = len;
return w;
}
//(2)类串与c串连接
String String::operator+(const char* c)const
{
String w;
int len = size + strlen(c);
delete[] w.str;
w.str = new char[len + 1];
if(!w.str)
Error("String: overflow!");
strcpy(w.str, c);
strcat(w.str, str);
w.size = len;
return w;
}
//(3)c串与类串连接
//注意:friend只能出现在友元函数的声明中,而不能出现在友元函数的实现中
String operator+(const char* c, const String& s)
{
String w;
int len = strlen(c) + s.size;
delete[] w.str;
if(!w.str)
s.Error("String: overflow!");//这里可以用Error私有函数是因为这里是友元函数
strcpy(w.str, c);
strcpy(w.str, s.str);
w.size = len;
return w;
}
//7.关系运算符重载
bool String::operator==(const String& s)
{
return strcmp(str, s.str) == 0;
}
bool String::operator==(const char* c)
{
return strcmp(str, c) == 0;
}
bool operator==(const char* c, const String& s)
{
return strcmp(c, s.str) == 0;
}
//8.求子串--从下表id开始读取num个字符组成新的字符串,作为返回值
//步骤:
//(1):创建新的字符串s,并修改新串的字符串空间为num+1
//(2):将原串中的子串字符逐个赋给新串
String String::SubStr(int id, int num)const
{
int len = size;
int left = len - id,i;
if(id<0||id>len-1)
Error("id id illegal!");
if (num <= 0 || num > left)
Error("num is illegal!");
//步骤(1)
String s;
delete[] s.str;
s.str = new char[num + 1];
if (!s.str)
Error("overflow!");
s.size = num;
//步骤(2)
char* p = str + id;
char* p1 = s.str;
for (i = 1; i <= num; i++)
*p1++ = *p++;
*p1 = '\\0';
return s;
}
//9.子串插入---在原串位置id处插入串s
//步骤:
//(1):扩展原串的字符串空间
//(2):将原串的结束符到下标id的字符依次后移
//(3):在下标id出插入串s
String& String::Insert(int id, const String& s)
{
char* p, * p1, * buf;
int len = size;//原串长度
int len1 = s.size;//插入串的长度
int left = len - id;//需要移动的字符个数
if (id<0 || id>len)
Error("id is illegal!");
//步骤(1)
buf = str;//先保存原串的字符串
str = new char[len + len1+1];//扩展原串的字符串空间
if (str == NULL)
Error("overflow!");
//将原串拷贝过来,并释放原串的内存
strcpy(str, buf);
delete[] buf;
//步骤(2)
p = str + len;//p指向原串的结束符
p1 = p + len1;//指向移动的终点,移动距离是插入串s的长度
for (int i = 1; i <= left+1; i++)
*p1-- = *p--; //从后往前挨个元素往后移动一格
//步骤(3)
p = str + id;
p1 = s.str;
while (*p1 != '\\0')
*p++ = *p1++;
//更新size大小
size = len + len1;
return *this;
}
//10.子串的删除---从原串下标id起,开始连续删除num个字符
//算法思想:原串分为前,中,后三段,中间是待删除的子串,前后连接
//步骤:
//(1)在原串中删除子串
//(2)暂存删除后的原串的字符串
//(3)重新分配原串的字符串空间,将暂存的字符串复制到原串,并释放原字符串空间
String& String::Erase(int id, int num)
{
char* p, * q, * buf;
int len = size;
int left = len - id;//最多删除的字符个数
if (id<0 || id>len - 1)
Error("id is illegal!");
if (num <= 0 || num > left)
Error("num is illegal!");
//步骤(1)
p = str + id;//删除的子串的下标起始位置
q = str + id + num;//删除子串的最后一个字符下标位置
//这里用后端字符覆盖中段字符
while (*q != '\\0')
*p++ = *q++;
*p = '\\0';//更新字符串结尾标志
//步骤(2)
buf = str;
//步骤(3)
len = strlen(buf);
str = new char[len + 1];
if (str == NULL)
Error("overflow! ");
strcpy(str, buf);
size = len;
delete[] buf;
return *this;
}
//11.下标运算符重载
char& String::operator[](int id)
{
if (id<0 || id>size - 1)
Error("operator[]:id is illigeal");
return *(str + id);
}
const char& String::operator[](int id)const
{
if(id<0||id>size-1)
Error("operator[]:id is illigeal");
return *(str + id);
}
//12.字符查找---从某一位置开始寻找某一个字符首次出现的位置,如果找到,则返回其下标,否则返回-1
//从id开始查找字符首次出现的位置
int String::Find_First_Of(char ch, int id)const
{
int start = id;
char* p;
if (start<0 || start>size - 1)
Error("Start is illegal!");
p = str + start;
while (*p!= '\\0')
{
if (*p == ch)
break;
else
{
++start;
p++;
}
}
return *p == '\\0' ? -1 : start;
}
//13.重载输入输出运算符
istream& operator>>(istream& istr, String& s)
{
char buf[256];//256是输入缓冲区长度
istr >> buf;
s = buf;//调用转换赋值运算符
return istr;
}
ostream& operator<<(ostream& ostr, const String& s)
{
ostr << s.str;
return ostr;
}
//14.readstring读取的字符串可以含空格,以dilemiter表示的字符为读取结束符号,默认读取结束符号是换行符
int String::ReadString(istream& istr, char delimiter)
{
char buf[256];
int token = -1;
if (istr.getline(buf, 256, delimiter))
{
*this = buf;//调用转换赋值运算符重载
token = size;
}
return token;
}
//下面是全篇最难点:模式匹配
//从一类串的某一个下标位置开始查找一个子串,称为模式匹配,前者称为原串,后者称为模式串
//算法思想:
//把模式串分为首字符,尾字符和首位字符中间的子串。
//首先在原串中查找模式串的首字符,然后在原串中查找与模式串尾字符位置对应的字符进行比较
//如果相等,就比较他们的中间子串。
//如果中间子串相等,就结束算法.
int String::Find_First_Of(const String& s, int id)const
{
int len = s.size;//模式串长度
int end = size - 1;//原串的尾字符
//步骤如下:
//1.取模式串的首字符和尾字符分别存储在变量first和last中
char first = s[0]; char last = s[len - 1];
int firstid, lastid;
String mid, cs;
//2.从id位置开始,在原串中查找模式串的首字符,用firstid表示这个字符的下标,
//然后在原串中计算与模式串尾字符位置对应的字符的下标lastid
if (len > 2)
{
mid = s.SubStr(1, len - 2);
}
firstid = Find_First_Of(first, id);//获取模式串第一个字符在原串中首次出现的位置的下标
lastid = firstid + len - 1;
//3.因为下标lastid没有超过原串的上界,所以取该下标的字符与模式串尾字符比较.
//如果相等以上是关于stl中String类的实现的主要内容,如果未能解决你的问题,请参考以下文章
C++初阶:STL —— stringstring类 | 浅拷贝和深拷贝(传统写法和现代写法) | string类的模拟实现
C++初阶:STL —— stringstring类 | 浅拷贝和深拷贝(传统写法和现代写法) | string类的模拟实现