这里直接开一个新空间 把传过来的字符内容赋值到 _str上
用const 不管是const和非const 都可以通用
初始化的时候要放\0到 字符里面
如果不放\0那么 穿空串会报错
string(const char* str = "")
{
_size = strlen(str);
_str = new char[_size + 1];
_capacity = _size;
strcpy(_str, str);
}
把传过来的 对象 用新空间来接受 把 新空间赋给 _str
开空间是为了让 _str有足够的空间来接受
string(const string& str)
{
_str = new char[str._capacity + 1];
strcpy(_str, str._str);
_size = str._size;
_capacity = str._capacity;
}
因为查看长度是不会改变的,所以用const来定义
size_t size() const
{
return _size;
}
const char* c_str() const
{
return _str;
}`
用引用传值,来减少时间
下标引用需要重载2个版本,一个是const的版本,一个是非const的
const的是给一些不需要改变的成员函数和用const定义的对象使用
非const 是给一些需要改变下标内的成员函数和普通对象使用
char& operator[](size_t pos)
{
assert(pos <= _size);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(pos <= _size);
return _str[pos];
}
类型间的转化
const char* c_str() const
{
return _str;
}
在string中 迭代器iterator其实就是指针
iterator需要重载 const和非const的版本
begin: 指向 对象的开始位置
end: 指向 对象的末尾位置
同样begin和end也需要重载2个版本
重载2个版本是因为有静态变量
//string 迭代器
typedef char* iterator;
//访问静态变量的话那么也只能读不能改
typedef const char* const_iterator;
const_iterator begin() const
{
return _str;
}
//指针+数字=*(下标+数字)
const_iterator end() const
{
return _str + _size;
}
iterator begin()
{
return _str;
}
//指针+数字=*(下标+数字)
iterator end()
{
return _str + _size;
}
void print_str(const string& str)
{
for (size_t i = 0; i < _size; i++)
{
cout<<str[i]<<" ";
}
cout << endl;
}
如果需要扩容的对象原本可存长度小于需要的长度那么直接扩容
如果是想缩容的话 我们不理会
void reserve(size_t n = 0)
{
//如果n>_capacity 那么开辟一块新空间 把 str里面的值拷贝给tmp
// 把旧空间释放
// 把tmp 赋给 _str
if (n > _capacity)
{
char* tmp = new char[n+1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
把一个字符直接尾插到对象里面
同样如果位置不够也是需要扩容的
void push_back(char str)
{
assert(str);
if (_size == _capacity)
{
size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newCapacity);
}
_str[_size++] = str;
_str[_size] = '\0';
}
把一个字符串插入到对象里面
如果位置不够也需要扩容
void append(const char* str)
{
assert(str);
size_t pos = strlen(str);
//如果 插入后越界就会出问题
if (_size + pos >= _capacity)
{
reserve(_size+pos);
}
strcpy(_str + _size, str);
_size += pos;
}
+=是可以插入一个字符和一个字符串
所以要重载2个版本
string& operator+= (char c)
{
push_back(c);
return *this;
}
string& operator+= (const char* s)
{
append(s);
return *this;
}
开辟一个新空间 让传过来的对象 赋值到 新空间上
把_str指向新空间
把旧的空间释放
string& operator=(const string& str)
{
//如果拷贝的时候 str的内容长度大于 this的内如长度
//那么则需要 新开辟一个空间
//让这个 空间让 str内容一样
//释放原来的旧this 内容
//把this指向那段新开辟的空间
if (this != &str)
{
char* tmp = new char[str._capacity + 1];
strcpy(tmp, str._str);
delete[] _str;
_size = str._size;
_capacity = str._capacity;
_str = tmp;
}
return *this;
}
因为是需要插入一个字符或者字符串
那么肯定是需要重载2个版本的
还要判断扩容,以及是否插入之后会越界
void insert(size_t pos, char ch)
{
assert(pos < _size);
if (_size == _capacity)
{
size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newCapacity);
}
//这里要指向 size+1 不然的话 有可能pos ==1 end会变成 -1
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch;
_size++;
}
//在pos位置插入一个字符串
void insert(size_t pos, const char * s)
{
assert(pos < _size);
size_t sz = strlen(s);
if (_size + sz > _capacity)
{
reserve(_size + sz);
}
size_t end = _size;
//把 数组中末尾位置的数据挪动到end+sz位置处
//给s数组 预留sz个位置
while (end >= (int)pos)
{
_str[end+sz] = _str[end];
end--;
}
strncpy(_str + pos, s,sz);
_size += sz;
}
如果从pos+len >原对象的长度的话那么 直接 插入一个\0到pos位置就可以了
如果不大于 那么就需要把 _str+pos+len位置的给到 _str+pos位置
void erase(size_t pos, size_t len = npos)
{
assert(pos <= _size);
//如果大于或 等于 _size - pos 的长度 那么 直接插入一个'\0' 因为他不管怎么样 都是删完
if (len==npos||npos >= _size - pos)
{
_str[pos] = '\0';
}
else
{
//这里拷贝的时候会从pos位置开始 拷贝 pos+len位置的值到的结束
strcpy(_str + pos, _str + pos+len);
_size -= len;
}
}
直接调用库里面的模版
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
size_t find(char ch, size_t pos = 0)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
}
找到子串返回 子串在 _str 的起始坐标
size_t find(const char * str,size_t pos = 0)
{
//如果找到子串赋给tmp
const char * tmp = strstr(_str+pos, str);
if (tmp == nullptr)
{
return npos;
}
//如果找到了 返回下标
//指针-指针=他们之间的差值
else
{
return tmp - _str;
}
}
需要开新空间接受这段字符串
返回值的时候如果没有写拷贝构造那么他会报错
原因是因为他指向的那块空间和函数里的新开辟一样
如果出了函数那么 创造的那个新空间会调用析构函数
那么会出现指向野指针的情况
string substr(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
size_t end = pos + len;
if (len == npos||end>=_size)
{
end = _size;
}
string str;
//开空间 让str可以存储
str.reserve(end - pos);
for (size_t i = 0; i < end; i++)
{
str += _str[i];
}
//这里要调拷贝构造才能正常返回
//如果这么没写拷贝构造
//那么会默认浅拷贝(值拷贝)
//浅拷贝:让 临时拷贝的对象指向跟str一样的空间
//但是出了函数 str会 析构
//会把临时对象指向的空间变成 野指针
return str;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
流插入(>>)的时候需要定义一个新的数组和一个字符
用字符存到数组里面
把数组尾插到串过来的对象里面
最后在 数组的尾部在插入一个\0来终止字符
如果直接插入的话会出现报错
这个报错就是出了这个函数这个 传过来的会调用析构
istream& operator>> (istream& in, string& str)
{
char ch = in.get();
char arr[128];
int a = 0;
while (ch!=' '&&ch!='\n')
{
arr[a++] = ch;
if (a == 127)
{
arr[a] = '\0';
str += arr;
a = 0;
}
ch = in.get();
}
if (a != 0)
{
arr[a] = '\0';
str += arr;
}
return in;
}
直接用for来打印
ostream& operator<< (ostream& os, const string& str)
{
for (size_t i = 0; i < str.size(); i++)
{
os << str[i];
}
return os;
}