// 04_字符串的顺序结构存储.cpp
/**
* 1. 串:串是由零个或多个字符组成的有限连续序列。
* 2. 串的逻辑结构与线性表极为相似,区别仅在于串的数据对象约束为字符集。
* 3. 串的数据结据成员包括存放串的字符数组及存放串的长度。
* 4. 串的基本操作包括构造,析构,添加,删除,搜索.
**/
/**
* -> 编写过程中网友提醒注意:
* 1. 友元函数不属于任何一个类,定义时不能使用String String:perator+();去掉String::
* 2. 不要返回一个临时变量或者另外一个函数的局部变量
* 3. 复制函数最好使用strcpy_s(),为了安全,因为有指定复制的大小,链接函数也一样,使用strcat_s()
* 4. 使用复制函数指定大小时,new多大的空间就指定多大的空间,不要size+1,运行时会出错,我的代码里已经改了,可以对照一下
**/
#include <string>
#include <iostream>
using namespace std;
// ---------------------------------- String 类的声明 ----------------------------------
class String
{
private:
char * p; // 存储串的字符数组
int size; // 串的长度为size - 1
public:
// 构造与析构
String(); // 构造函数,将串初始化为空串
String(char * str); // 构造函数,用字符串对象来初始化
String(const String & obj); // 构造函数,用串对象来初始化
~String() {delete p;}
// 查找,提取,插入,删除,复制——基本操作
int strSize() {return size - 1;}
int find(String & pat, int start); // 在串中从start位置开始查长一个与串pat相同的子串,若找到返回其位置
void insert(const String & obj, int pos); // 在串对象的pos位置插入子串obj
void del(int pos, int length); // 删除从串对象的pos位置开始并且长度为length的子串
// 强制类型转换,运算符重载
operator char * () {return p;} // 把串对象转换成C++串
char & operator [] (int n){ return p[n];} // 下标运算符重载
friend ostream & operator << (ostream & stream, string & obj); // 字符串输出流重载
friend istream & operator >> (istream & stream, string & obj); // 字符串输入流重载
String operator = (String & obj); // 赋值串对对象
String operator = (char * s); // 赋值C++串
String operator + (String & obj); // 两个串对象的连接
String operator + (char * s); // 串对象与C++串的连接
friend String& operator + ( char * s, String & obj); // C++串与串对象的连接
// 串对象之间的关系运算符
int operator == (String & obj) {return !strcmp(p, obj.p);}
int operator != (String & obj) {return strcmp(p, obj.p);}
int operator < (String & obj) {return strcmp(p, obj.p) < 0;}
int operator > (String & obj) {return strcmp(p, obj.p) > 0;}
int operator <= (String & obj) {return strcmp(p, obj.p) <= 0;}
int operator >= (String & obj) {return strcmp(p, obj.p) >= 0;}
// 串对象之间的关系运算符
int operator == (char * s) {return !strcmp(p, s);}
int operator != (char * s) {return strcmp(p, s);}
int operator < (char * s) {return strcmp(p, s) < 0;}
int operator > (char * s) {return strcmp(p, s) > 0;}
int operator <= (char * s) {return strcmp(p, s) <= 0;}
int operator >= (char * s) {return strcmp(p, s) >= 0;}
};
// ---------------------------------- String 类的定义 ----------------------------------
/**
* -> 构造函数的实现
* 1. 第一个构造函数:构造一个空串,先通过预分配一个有一个字符的内容空间,再放进一个NULL
* 2. 第二个构造函数:通过引入一个字符串,然后改变size,分配空间,初始化,构造完成。
* 3. 第三个构造函数:引入string型函数,跟第二个原理几乎一样。
**/
String::String()
{ // 初始化时构造一个空串
size = 1; // 申请一个字节空间用来存放串结束标志NULL
p = new char[size];
if (p = NULL)
{
std::cerr << "\n 内存分配错误,程序即将退出!";
exit(1);
}
*p = NULL;
}
String::String(char * str)
{ // 用带引号的串初始化
size = (int)strlen(str) + 1;
p = new char[size];
if (p == NULL)
{
std::cerr << "\n 内存分配错误,程序即将退出!";
exit(1);
}
strcpy_s(p,size,str); // 初始化为串str size不需要加1
}
String::String(const String & obj)
{
size = obj.size;
p = new char[size + 1]; // 申请是多一个字节用来存放串结束标志 NULL
if (p == NULL)
{
std::cerr << "\n 内存分配错误,程序即将退出!";
exit(1);
}
strcpy_s(p,size, obj.p); // 初始化串obj.p
}
/**
* -> 串的连接
* 1. 第一种“+”号运算符重载:两个串对象连接。
* 2. 第二种“+”号运算符重载:与带引号的char连接。
* 3. 第三种“+”号运算符重载:友元类,即使带引号的char放在前面也能连接。
* -> 连接方法
* 1. 计算分配空间
* 2. 再插入原字符串
* 3. 添加新字符串
**/
String String::operator + (String & obj)
{ // 两个串对象的连接,结果仍为String类的对象
String temp;
temp.size = size + obj.size - 1;
delete [] temp.p;
temp.p = new char [temp.size];
if (temp.p == NULL)
{
std::cerr << "\n 内存分配错误,程序即将退出!";
exit(1);
}
strcpy_s(temp.p,temp.size, p);
strcat_s(temp.p, size ,obj.p);
return temp;
}
String String::operator +(char * s)
{
String temp;
temp.size = (int)(size + strlen(s));
delete [] temp.p;
temp.p = new char[temp.size];
if (temp.p == NULL)
{
std::cerr << "\n 内存分配错误,程序即将退出!";
exit(1);
}
strcpy_s(temp.p,temp.size, p);
strcat_s(temp.p,temp.size, s);
return temp;
}
String& operator +(char * s,String & obj)
{
/*String *temp;
temp.size = strlen(s) + strlen(obj.size) + 1; // 串obj.p的长度 + 串s的长度 + 1
temp.p = new char[temp.size];
if (temp.p == NULL)
{
std::cerr << "\n 内存分配错误,程序即将退出!";
exit(1);
}
strcpy(temp.p, s); // 把s中存储的串复制给temp.p
strcpy(temp.p, obj.p); // 把temp.p的串与obj.p的串连接起来赋给temp.p
return temp;*/
realloc(obj.p,(strlen(s)+strlen(obj.p)+1));
strcat_s(obj.p,(strlen(s)+strlen(obj.p)+1),s);
return obj;
}
/**
* -> 赋值操作,通过赋值运算符
* 1. 第一种,通过原字符串赋值给新字符串
* 2. 第二种,通过char型字符串直接赋值给新字符串
* -> 赋值原理(跟插入操作的区别不大)
* 1. 删除原字符串的内容
* 2. 先为新字符串分配空间
* 3. 将字符串复制到新空间
**/
String String::operator = (String & obj)
{
size = obj.size;
delete [] p;
p = new char[size];
if (p == NULL)
{
std::cerr << "\n内存分配错误,程序即将退出!";
exit(1);
}
strcpy_s(p,size,obj.p);
return *this;
}
String String::operator = (char * s) // 赋值C++串
{
size = (int)strlen(s) + 1;
delete [] p;
p = new char [size];
if (p == NULL)
{
std::cerr << "\n内存分配错误,程序即将退出!";
exit(1);
}
strcpy_s(p,size,s);
return * this;
}
// ---------------------------------- 各种基本操作 ----------------------------------
/**
* -> 插入操作 方法
* 1. 先为字符串分配空间
* 2. 然后给字符串复制进插入位置前的字符
* 3. 之复将字符串需要添加的内容复制进新的字符串
* 4. 最后将旧字符串的内容添加入新的字符串
**/
void String::insert(const String & obj, int pos) // 在串对象的pos位置插入子串obj
{
if (pos > size)
{
cout << "插入位置选择错误!";
return;
}
String temp;
temp.size = obj.size + size - 1;
temp.p = new char [temp.size];
int nowsize(0);
for (int i = 0; i < pos; ++i,++nowsize)
{
temp[nowsize] = p[i];
}
for (int i = 0; i != obj.size; ++i, ++nowsize)
{
temp.p[nowsize] = obj.p[i];
}
for (int i = pos; i != size; ++i, ++nowsize)
{
temp[nowsize] = p[i];
}
(*this) = temp;
return;
}
/**
* -> 删除操作 方法
* 1. 直接将字符串需要删除的字符内容后面一位开始移动复制字符到前面需要删除的内容,覆盖清除
**/
void String::del(int pos, int length) // 删除从串对象的pos位置开始并且长度为length的子串
{
if (pos < 0 || pos > size)
{
cout << "定位错误,删除失败。";
return;
}
int i = pos + length + 1;
int k = pos;
for (; i < size; ++k, ++i)
{
p[k] = p[i];
}
return;
}
/**
* -> 查找函数:串的模式匹配
* 1. 这种算法,可以这样描述,一个主串,一个模式串,现要从主串中找出包含了模式串
* 2. 模式匹配的简单算法的思想是:
* 将模式串的首字符开始与主串的首字符比较,
* 然后,如果两者对应,则继续比较模式串及主串的下一个字符。
* 如果两者不对应,则将模式串右滑到刚刚在模式串中比较首字符的第一位的对应主串位置的下一位中去。
**/
int String::find(String & pat, int start) // 在串中从start位置开始查长一个与串pat相同的子串,若找到返回其位置
{
int newstringsize ( size - start ); // 计算出新的需要对比的字符串总长度
int sourcei(start); // 原string需要比较的变量的下标[比较目标的下标]
int comparei(0); // 新的用于pat变量的比较的下标[比较量的下标]
if ( newstringsize < 0 || pat.strSize() == 0 || pat.strSize() > size - 1)
return -1; // 在需要对比的变量为空的情况下,直接返回程序。
while ( sourcei < newstringsize && comparei < pat.strSize())
{
if ( p[sourcei] == pat[comparei]) // 比较两字符是否相等,若相等,则直接比较下一字符
{
++sourcei;
++comparei;
}
else
{
sourcei = sourcei - comparei + 1; // 减去曾比较过的长度,得到重新需要比较的位置,加1,比较新位置
comparei = 0;
}
}
if (sourcei == pat.strSize())
return sourcei - pat.strSize(); // 用成功匹配比较后的长度再减去需要比较的那个字符串的长度,结果就是原字符串的位置了。
else
return -1; // -1代表未能找到匹配的字符串
}
int main()
{
return 0;
}
(2011.11.28) 04_字符串的顺序结构存储.cpp
最新推荐文章于 2023-01-16 16:38:41 发布