string类的模拟实现
本文只是对部分函数进行分析和代码展示,具体代码我已经上传到GitHub上面了
GitHub链接:https://github.com/Sveter/CPlusPlus
- string类
- string是表示字符串的字符串类
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
- 不能操作多字节或者变长字符的序列。
模拟实现
- String.h
#ifndef __STRING_H__
#define __STRING_H__
#include <iostream>
#include <string.h>
unsig namespace std;
class String
{
//为了实现迭代器
public:
typedef char* Iterator;
public:
//默认成员函数
//构造函数以及构造函数的重载
String()
{}
String(const char* str)
{
if (nullptr == str)
{
str = "";
}
_size = strlen(str);
_str = new char[_size + 1];
_capacity = _size;
strcpy(_str, str);
}
String(const char* str, size_t size)
{
if (size < 0)
{
cout << "size error" <<endl;
exit(EXIT_SUCCESS);
}
_size = size;
_str = new char[_size + 1];
_capacity = _size;
strncpy(_str, str, size);
if (size < strlen(str))
{
_str[_size] = 0;
}
}
//拷贝构造函数
//采用深拷贝的传统写法
String(const String& s)
:_str(new char[s._capacity+1])
,_capacity(s._capacity)
,_size(s._size)
{
strcpy(_str, s._str);
}
//深拷贝的现代版写法
//String(const String& s)
//{
// String strTmp(s._str);
// swap(_str, strTmp);
//}
//赋值运算符的重载
String& operator=(const String& s)
{
if (&s != this)
{
char* pStr = new char[s._capacity + 1];
strcpy(pStr, s._str);
delete[] _str;
_str = pStr;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
//析构函数
~String()
{
if (_str)
{
delete[] _str;
_str = nullptr;
_capacity = 0;
_size = 0;
}
}
//成员函数
//1. 迭代器
Iterator Begin();
Iterator End();
//反向查找
Iterator RBegin();
Iterator REnd();
//2. 增删改查
void PushBack(char c); //尾插字符
void Append(const char* str); //追加字符串
String& Insert(size_t pos, char c); //在指定位置插入字符
String& Insert(size_t pos, const char* str); //在指定位置插入字符串
String& Erase(size_t pos, size_t len); //删除指定位置,指定长度的内容
void Resize(size_t newSize, char c = '0'); //调整大小
void Reserve(size_t newCapacity); //扩容
int Size()const; //查询字符串大小
int Capacity()const; //查询容量
bool Empty()const; //判断是否为空
void Clear(); //清空
int Find(char c, size_t pos = 0); //查找字符第一次出现的位置
int rFind(char c); //从后往前查找字符第一次出现的位置
void Swap(String& s); //字符串交换
String SubStr(size_t pos, size_t size); //截取字符串
const char* C_Str()const; //以C的形式输出
//3. []的重载
char& operator[](size_t index);
const char& operator[](size_t index)const;
//4. 运算符的重载
bool operator<(const String& s);
bool operator<=(const String& s);
bool operator>(const String& s);
bool operator>=(const String& s);
bool operator==(const String& s);
bool operator!=(const String& s);
String operator+(char c); //+符号的重载
String operator+(const char* str)
String operator+=(char c) //+=符号的重载
String operator+=(const char* str)
//<<&>>的重载
friend ostream& operator<<(ostream& _cout, const String& s)
friend istream& operator>>(istream& _cin, String& s)
private:
char* _str;
size_t _capacity;
size_t _size;
//find函数没找到的返回值
const static int npos;
};
const int String::npos = -1;
#endif //!__STRING_H__
-
在这里我仅对部分成员函数分析
-
1. 构造函数和拷贝构造函数
- 构造函数是创建一个对象,然后给_str变量申请内存,再设置好_size和_capacity变量
- 拷贝构造函数这里分为浅拷贝和深拷贝,我们用深拷贝,在编译器中默认是浅拷贝,最终导致的问题是,共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃
浅拷贝和深拷贝
- 2. 赋值运算符重载
赋值运算符重载和拷贝构造函数存在同样的问题,在这里我们用的也是深拷贝的方式
- Insert()
- 先判断是否需要扩容
- 把pos位置之后的字符都向后挪一位,在pos位置插入指定字符
String& Insert(size_t pos, char c)
{
if (_size + 1 >= _capacity)
{
Reserve(_capacity * 2);
}
_str[_size + 1] = '\0';
for (int i = _size; i > pos; i--)
{
_str[i] = _str[i - 1];
}
_str[pos] = c;
++_size;
return *this;
}
- Erase()
第一种,pos位置之后的字符个数大于n,pos之后全删
第二种,pos位置之后的字符个数小于n,则我们需要在pos之后删除n个元素
String& Erase(size_t pos, size_t len)
{
//删除的数据未超过字符串长度
if (pos + len <= _size)
{
while (_str[pos + len] != '\0')
{
_str[pos] = _str[pos + len];
pos++;
}
_str[pos] = _str[pos + len];
_size = strlen(_str);
}
else
{
_str[pos] = _str[_size];
_size = strlen(_str);
}
return *this;
}
测试
void TestString1()
{
String s1;
String s2("hello world");
String s3(s2);
s1 = s3;
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
}
void TestString2()
{
String s1("hello");
s1.PushBack(' ');
s1.PushBack('1');
s1.Append("31");
s1 += '4';
cout << s1 << endl;
cout << s1.Size() << endl;
cout << s1.Capacity() << endl;
// 利用迭代器打印string中的元素
auto it = s1.Begin();
while (it != s1.End())
{
cout << *it++;
}
cout << endl;
String s2("hello world!!!");
s1.Swap(s2);
cout << s1 << endl;
cout << s2 << endl;
}
void TestString3()
{
String s("hello");
cout << s << endl;
cout << s.Size() << endl;
cout << s.Capacity() << endl;
s.Resize(10, 'a');
cout << s << endl;
cout << s.Size() << endl;
cout << s.Capacity() << endl;
s.Resize(20);
cout << s << endl;
cout << s.Size() << endl;
cout << s.Capacity() << endl;
s.Resize(5);
cout << s << endl;
cout << s.Size() << endl;
cout << s.Capacity() << endl;
s.Reserve(50);
cout << s << endl;
cout << s.Size() << endl;
cout << s.Capacity() << endl;
}
void TestString4()
{
String s1("abcdefg");
String s2 = s1;
cout << s1 << endl;
s1.Insert(2, 'h');
cout << s1 << endl;
s1.Insert(2, "123");
cout << s1 << endl;
s2.Erase(2, 5);
cout << s2 << endl;
}
void TestString5()
{
String s1 = "abcdef";
String s2 = "abcdh";
cout << (s1 < s2) << endl;
cout << (s1 > s2) << endl;
cout << (s1 == s2) << endl;
cout << (s1 != s2) << endl;
cout << (s1 >= s2) << endl;
cout << (s1 <= s2) << endl;
}
int main()
{
cout << "Test1: 默认成员函数" << endl;
TestString1();
cout << endl;
cout << "Test2: Iterators + Modifiers" << endl;
TestString2();
cout << endl;
cout << "Test3: Capacity" << endl;
TestString3();
cout << endl;
cout << "Test4: Modifiers" << endl;
TestString4();
cout << endl;
cout << "Test5: 比较" << endl;
TestString5();
cout << endl;
return 0;
}