基础结构
string依然是一个线性的结构,所以说在实现的时候和以前实现的线性结构差不了多少,总体还是我们的老三样------_str,_size和_capacity。
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include <assert.h>
namespace yu
{
class String
{
public:
private:
char* _str;
size_t _capacity;//注意_capacity不包含'\0'
size_t _size;
};
}
本次实现string的时候,我们把一些简单的小函数在.h中直接定义,所以我们要注意链接错误,.h文件中的函数,如果定义在类里面,默认是内敛的,不会放到符号表,不用担心链接错误,但是如果是在类外面定义的函数,.h文件在test.cpp和string.cpp中都包含了一份,就会有两个这个函数,导致链接错误。
解决方案,将函数设置内敛或者是静态函数,不会放到符号表,只在当前文件可见。当然最简单的办法依旧是做声明与定义分离。
string类的基础成员函数
构造函数
不带参的构造函数
cpp
String()
:_str('\0')
,_capacity(0)
,_size(0)
{
}
带参的构造函数
cpp
String(const char* str)
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
缺省参数构造函数
当然我们也可以把这两个糅合一下,使用缺省参数来写一个构造函数
cpp
String(const char* str = "")//注意这里不要输入空格,空语句自带'\0'
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
析构函数
cpp
~String()
{
delete[] _str;
_str = nullptr;//释放之后置为空指针预防野指针
_capacity = _size = 0;
}
拷贝构造函数
对于指向资源的类类型对象我们必须自己实现拷贝构造函数来实现深拷贝,默认生成的拷贝构造函数是值拷贝,详情见(【c++】类和对象 (中) (类的默认成员函数)-CSDN博客)
cpp
String(const String &s)
{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
当然还有一些其他的写法,我们可以把交换的功能封装到一个函数,然后将值赋到临时变量之后再整体操作
cpp
void Swap(String& s)
{
swap(_capacity,s._capacity);
swap(_str, s._str);
swap(_size, s._size);
}
String(const String& s)
{
String tmp(s._str);
Swap(tmp);
}
基础函数
cpp
//用这个返回字符串
const char* c_str() const
{
return _str;
}
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity;
}
运算符重载
[]重载
重载[]之后,就像数组一样访问string。
cpp
char& operator[](size_t pos)
{
assert(pos <= _size);
char& n = _str[pos];
return n;
}
const char& operator[](size_t pos) const
{
assert(pos <= _size);
char& n = _str[pos];
return n;
}
赋值运算符重载
cpp
String& operator=(const String& s)
{
if (this != &s)
{
delete[] _str;
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
迭代器
cpp
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
我们来测试一下
cpp
void test01()
{
String s1("hello !");
String s2(s1);
String::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it ;
it++;
}
cout << endl;
for (auto i: s1)
{
cout << i;
}
cout << endl;
}
范围for的底层就是迭代器
string的修改器
reserve()
cpp
void String::reserve(size_t n)
{
if (n > _capacity)
{
char *str = new char[n+1];
strcpy(str, _str);
delete[] _str; //释放原来的空间
_str = str;
_capacity = n;
}
}
push_back()
cpp
void String::push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';//不加这一行没有\0,不会停下来
}
append()
cpp
void String::append(const char* str)
{
size_t len = strlen(str);
if (_size + len >_capacity)
{
if (_size + len > 2 * _capacity)
{
reserve(_size + len + 1);
}
if (_size + len <= 2 * _capacity)
{
reserve(2 * _capacity);
}
}
strcpy(_str+_size, str);
_size += len;
}
+=运算符重载
单个字符
cpp
String& String::operator+=(char ch)
{
push_back(ch);
return *this;
}
字符串
cpp
String& String::operator+=(const char* str)
{
append(str);
return *this;
}
insert()
单个字符
cpp
void String::insert(size_t pos, char ch)
{
assert(pos < _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
//挪动数据
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch;
_size++;
}
字符串
cpp
void String::insert(size_t pos, char* str)
{
//pos 不能超出范围
assert(pos <= _size);
int len = strlen(str);
//看看现在的空间够不够
if (pos + len > _capacity)
{
//如果需要的不大于两倍容量按两倍开,不然要好多开好多
reserve(pos + len > 2 * _capacity ? pos + len : 2 * _capacity);
}
size_t end = _size + len;
//空间开好了,来挪动数据,把前面的挪到后边
while (end > pos + len - 1)
{
_str[end] = _str[end - len];
end--;;
}
//插入数据
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = str[i];
}
//处理完了,最后一步
_size += len;
}
erase()
cpp
void String::erase(size_t pos, size_t len)
{
assert(pos < _size);
//不需要对空间进行操作,直接挪动数据
/*size_t end = _size - len;
for (size_t i = 1; i < len; i++)
{
_str[pos + i] = _str[end];
end++;
}*/
if (len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
for (size_t i = pos + len; i <= _size; i++)
{
_str[i - len] = _str[i];
}
_size -= len;
}
}
String的查找
单个字符
cpp
size_t String::find(char ch, size_t pos)
{
assert(pos < _size);
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
字符串
cpp
size_t String::find(const char* str, size_t pos)
{
assert(pos < _size);
const char* ptr = strstr(_str + pos, str);
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - _str;
}
}
substr()
cpp
String String::substr(size_t pos, size_t len)
{
assert(pos < _size);
if (len > _size - pos)//剩下的全取出来
{
len = _size - pos;
}
String sub;
sub.reserve(len);
for (int i = 0; i < len; i++)
{
sub += _str[pos + i];
}
return sub;//传值返回会在堆栈调用拷贝构造重新生成一个变量后再返回,没有写拷贝构造默认浅拷贝
}