resize 和 尾插(+= \ push_back \ append)
#pragma once
#include<assert.h>
namespace bit
{
class string
{
public:
//默认
/*string()
:_str(new char[1])
, _size(0)
, _capacity(0)
{
*_str = '\0';
}*/
//全缺省
//string(char* str = nullptr)--err,当什么都不传的时候下面strlen会对空指针解引用,遇到\0才停止会出错
//string(char* str = "\0")
string(char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// s1.swap(s2)
//因为size和capacity都要交换所以直接写一个交换函数比较方便
void swap(string& s)
{
::swap(_str, s._str);//调全局域(左边为空默认为全局)的swap
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
// 深拷贝的现代写法
// s2(s1)
string(const string& s)
:_str(nullptr)
{
string tmp(s._str);
//this->swap(tmp);
swap(tmp);
}
// s1 = s3
string& operator=(string s)
{
swap(s);
return *this;
}
~string()
{
delete[] _str;
_str = nullptr;
}
char& operator[](size_t pos)
{
assert(pos);
return _str[pos]; // *(_str+pos)
}
// reserve--开空间,reverse--逆置
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];//开空间
strcpy(tmp, _str);//把旧空间的数据拷贝到新空间
delete[] _str;//释放旧空间
_str = tmp;//_str指向新空间
_capacity = n;
}
}
void resize(size_t n, char ch = '\0')
{
if (n <= _size)
{
//缩容
_size = n;
_str[_size] = '\0';//五个字符放的位置是0-4,5是来放\0的
}
else
{
//空间不够先增容
if (n > _capacity)
{
reserve(n);
}
//从size开始填数据
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_size = n;
_str[_size] = '\0';
}
}
//尾插
void push_back(char ch)
{
//不能直接开两倍,如果原空间为0的时候会出错
if (_size >= _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
//添加一个字符串
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len >= _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
}
string& operator+=(char ch)
{
//this->push_back(ch);
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
private:
char* _str;
size_t _size;
size_t _capacity; // 不包含最后做标识的\0
};
void test_string2()
{
string s1("hello");
//s1.push_back(' ');
//s1.append("world");
s1 += ' ';
s1 += "world";
}
void test_string3()
{
string s1;//所以要写全缺省的不然传空的话strlen对空指针的解引用会报错
string s2(s1);
s1 += 'x';
s1 += "hello";
s1.resize(3);
s1.resize(8, 'x');
s1.resize(20, 'x');
}
}
test_string3()
insert/erase
#pragma once
#include<string.h>
#include<assert.h>
namespace bit
{
class string
{
public:
//默认
/*string()
:_str(new char[1])
, _size(0)
, _capacity(0)
{
*_str = '\0';
}*/
//全缺省
//string(char* str = nullptr)--err,当什么都不传的时候下面strlen会对空指针解引用,遇到\0才停止会出错
//string(char* str = "\0")
string(char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// s1.swap(s2)
//因为size和capacity都要交换所以直接写一个交换函数比较方便
void swap(string& s)
{
::swap(_str, s._str);//调全局域(左边为空默认为全局)的swap
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
// 深拷贝的现代写法
// s2(s1)
string(const string& s)
:_str(nullptr)
{
string tmp(s._str);
//this->swap(tmp);
swap(tmp);
}
// s1 = s3
string& operator=(string s)
{
swap(s);
return *this;
}
~string()
{
delete[] _str;
_str = nullptr;
}
//读、写
char& operator[](size_t pos)
{
assert(pos<_size);
return _str[pos]; // *(_str+pos)
}
// reserve--开空间,reverse--逆置
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];//开空间
strcpy(tmp, _str);//把旧空间的数据拷贝到新空间
delete[] _str;//释放旧空间
_str = tmp;//_str指向新空间
_capacity = n;
}
}
void resize(size_t n, char ch = '\0')
{
if (n <= _size)
{
//缩容
_size = n;
_str[_size] = '\0';//五个字符放的位置是0-4,5是来放\0的
}
else
{
//空间不够先增容
if (n > _capacity)
{
reserve(n);
}
//从size开始填数据
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_size = n;
_str[_size] = '\0';
}
}
//尾插
void push_back(char ch)
{
insert(_size, ch);
}
//添加一个字符串
void append(const char* str)
{
insert(_size, str);
}
string& operator+=(char ch)
{
//this->push_back(ch);
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
//insert--利用头插在任意位置插入字符
string& insert(size_t pos, char ch)
{
assert(pos <= _size);
//增容
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
/*int end = _size;
while (end >= (int)pos)
{
_str[end + 1] = _str[end];
--end;
}*/
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
++_size;
return *this;
}
//insert--插入字符串
string& insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len == 0)
{
return *this;
}
if (len + _size > _capacity)
{
reserve(len + _size);
}
size_t end = _size + len;
while (end >= pos + len)
{
_str[end] = _str[end - len];
--end;
}
for (size_t i = 0; i < len; ++i)
{
_str[pos + i] = str[i];
}
_size += len;
return *this;
}
string& erase(size_t pos, size_t len = npos)
{
assert(pos < _size);//不能删除\0
// 1、pos后面删完
if (len == npos || pos + len >= _size)//npos代表很大
{
_str[pos] = '\0';
_size = pos;
}
// 2、pos后面删除一部分
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
//可以让test_string4()直接打印
const char* c_str()
{
return _str;
}
private:
char* _str;
size_t _size;
size_t _capacity; // 不包含最后做标识的\0
//声明
static const size_t npos;
};
//定义
const size_t string::npos = -1;
void test_string4()
{
//插入字符
string s1("hello world");
s1.insert(5, 'x');
cout << s1.c_str() << endl;
s1.push_back('!');
cout << s1.c_str() << endl;
s1.insert(0, 'y');
cout << s1.c_str() << endl << endl;
//插入字符串
string s2("helloworld");
s2.insert(5, "bit");
cout << s2.c_str() << endl;
s2.insert(0, "hello");
cout << s2.c_str() << endl << endl;
//erase
string s3("helloworld");
cout << s3.c_str() << endl;
s3.erase(5, 3);
cout << s3.c_str() << endl;
s3.erase(5, 10);
cout << s3.c_str() << endl;
}
}
insert–注意在头部插入的问题
erase
遍历(for、迭代器、auto)
#pragma once
#include<string.h>
#include<assert.h>
namespace bit
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;//const迭代器
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin() const//const迭代器
{
return _str;
}
const_iterator end() const//const迭代器
{
return _str + _size;
}
//默认
/*string()
:_str(new char[1])
, _size(0)
, _capacity(0)
{
*_str = '\0';
}*/
//全缺省
//string(char* str = nullptr)--err,当什么都不传的时候下面strlen会对空指针解引用,遇到\0才停止会出错
//string(char* str = "\0")
string(char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// s1.swap(s2)
//因为size和capacity都要交换所以直接写一个交换函数比较方便
void swap(string& s)
{
::swap(_str, s._str);//调全局域(左边为空默认为全局)的swap
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
// 深拷贝的现代写法
// s2(s1)
string(const string& s)
:_str(nullptr)
{
string tmp(s._str);
//this->swap(tmp);
swap(tmp);
}
// s1 = s3
string& operator=(string s)
{
swap(s);
return *this;
}
~string()
{
delete[] _str;
_str = nullptr;
}
//读、写
char& operator[](size_t pos)
{
assert(pos<_size);
return _str[pos]; // *(_str+pos)
}
// 读
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos]; // *(_str+pos)
}
// reserve--开空间,reverse--逆置
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];//开空间
strcpy(tmp, _str);//把旧空间的数据拷贝到新空间
delete[] _str;//释放旧空间
_str = tmp;//_str指向新空间
_capacity = n;
}
}
void resize(size_t n, char ch = '\0')
{
if (n <= _size)
{
//缩容
_size = n;
_str[_size] = '\0';//五个字符放的位置是0-4,5是来放\0的
}
else
{
//空间不够先增容
if (n > _capacity)
{
reserve(n);
}
//从size开始填数据
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_size = n;
_str[_size] = '\0';
}
}
//尾插
void push_back(char ch)
{
//不能直接开两倍,如果原空间为0的时候会出错
if (_size >= _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
//添加一个字符串
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len >= _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
}
string& operator+=(char ch)
{
//this->push_back(ch);
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity;
}
private:
char* _str;
size_t _size;
size_t _capacity; // 不包含最后做标识的\0
};
//s1传给s不要用传值拷贝--会深拷贝--所以加引用 不改变--加const
void print(const string& s)
{
for (size_t i = 0; i < s.size(); ++i)
{
//const对象需要调用operator[],所以要增加一个const的版本的operator[]
//s[i] = 'x';——err
cout << s[i] << " ";
}
cout << endl;
//const对象,写const迭代器,begin和end都是要const
string::const_iterator it = s.begin();
while (it != s.end())
{
//*it += 1;--err
cout << *it << " ";
++it;
}
cout << endl;
}
void test_string5()
{
// 遍历 打印
string s1("hello world");
for (size_t i = 0; i < s1.size(); ++i)
{
//s1[i] += 1;
cout << s1[i] << " ";
}
cout << endl;
string::iterator it = s1.begin();
while (it != s1.end())
{
//*it -= 1;
cout << *it << " ";
++it;
}
cout << endl;
// 范围for的原理就是被替换成迭代器
//e是定义了一个变量,把*it赋值给它,e的改变不会影响*it
//加引用给e才能改变e的时候改变*it
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
print(s1);
}
}
输入、输出
#pragma once
#include<string.h>
#include<assert.h>
namespace bit
{
class string
{
public:
//默认
/*string()
:_str(new char[1])
, _size(0)
, _capacity(0)
{
*_str = '\0';
}*/
//全缺省
//string(char* str = nullptr)--err,当什么都不传的时候下面strlen会对空指针解引用,遇到\0才停止会出错
//string(char* str = "\0")
string(char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// s1.swap(s2)
//因为size和capacity都要交换所以直接写一个交换函数比较方便
void swap(string& s)
{
::swap(_str, s._str);//调全局域(左边为空默认为全局)的swap
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
// 深拷贝的现代写法
// s2(s1)
string(const string& s)
:_str(nullptr)
{
string tmp(s._str);
//this->swap(tmp);
swap(tmp);
}
// s1 = s3
string& operator=(string s)
{
swap(s);
return *this;
}
~string()
{
delete[] _str;
_str = nullptr;
}
//读、写
char& operator[](size_t pos)
{
assert(pos<_size);
return _str[pos]; // *(_str+pos)
}
// 读
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos]; // *(_str+pos)
}
// reserve--开空间,reverse--逆置
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];//开空间
strcpy(tmp, _str);//把旧空间的数据拷贝到新空间
delete[] _str;//释放旧空间
_str = tmp;//_str指向新空间
_capacity = n;
}
}
void resize(size_t n, char ch = '\0')
{
if (n <= _size)
{
//缩容
_size = n;
_str[_size] = '\0';//五个字符放的位置是0-4,5是来放\0的
}
else
{
//空间不够先增容
if (n > _capacity)
{
reserve(n);
}
//从size开始填数据
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_size = n;
_str[_size] = '\0';
}
}
//尾插
void push_back(char ch)
{
insert(_size, ch);
}
//添加一个字符串
void append(const char* str)
{
insert(_size, str);
}
string& operator+=(char ch)
{
//this->push_back(ch);
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity;
}
// insert--利用头插在任意位置插入字符
string& insert(size_t pos, char ch)
{
assert(pos <= _size);
//增容
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
++_size;
return *this;
}
//insert--插入字符串
string& insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len == 0)
{
return *this;
}
if (len + _size > _capacity)
{
reserve(len + _size);
}
size_t end = _size + len;
while (end >= pos + len)
{
_str[end] = _str[end - len];
--end;
}
for (size_t i = 0; i < len; ++i)
{
_str[pos + i] = str[i];
}
_size += len;
return *this;
}
//可以让test_string5()直接打印
const char* c_str()
{
return _str;
}
//为了在test_string6中输入字符的时候不会在原字符基础下输出
void clear()
{
_str[0] = '\0';
_size = 0;
}
private:
char* _str;
size_t _size;
size_t _capacity; // 不包含最后做标识的\0
//声明
static const size_t npos;
};
//定义
const size_t string::npos = -1;
//这里不需要写成友元
ostream& operator<<(ostream& out, const string& s)
{
// 不管字符数组中的内容是啥,size是多少,就要输出多少个有效字符
for (size_t i = 0; i < s.size(); ++i)
{
out << s[i];
}
return out;
}
istream& operator>>(istream& in, string& s)
{
s.clear();//先清理一下原字符串中的数据
char ch;
//in >> ch;
ch = in.get();//为了下面能拿到换行
while (ch != ' ' && ch != '\n')
{
s += ch;
//in >> ch;
ch = in.get();
}
return in;
}
istream& getline(istream& in, string& s)
{
s.clear();
char ch;
ch = in.get();
while (ch != '\n')
{
s += ch;
//in >> ch;
ch = in.get();
}
return in;
}
void test_string6()
{
string s1("hello world");
//cin >> s1;
cout << s1 << endl;
cout << s1.c_str() << endl;
// 一般场景下,以下两种输出没有差别,但是这种场景下有差别
string s2("hello world");
s2.resize(20);//后面全为\0
s2[19] = 'x';
// 不管字符数组中的内容是啥,size是多少,就要输出多少个有效字符
cout << s2 << endl;
// 不管实际字符串的长度,遇到\0就终止
cout << s2.c_str() << endl;
//char arr[5] = { 'h', 'e', 'l', 'l', 'o' };
//cout << arr << endl;
string s3;
cin >> s3;
cout << s3 << endl;
}
void test_string7()
{
string s1;
//cin >> s1;//以空格分割成不同的输入
//cout << s1 << endl;
getline(cin, s1);//可以包含空格
cout << s1 << endl;
}
}
两种输出字符的区别
test.cpp
#include <list>
using namespace std;
#include "string.h"
int main()
{
try
{
//bit::test_string2();
//bit::test_string3();
//bit::test_string4();
//bit::test_string5();
bit::test_string6();
bit::test_string7();
}
catch (const exception& e)//检查s1==s3的new是否失败
{
cout << e.what() << endl;
}
//因为operator【】中加了检查下标
//对于普通数组而言,越界读一般是检查不出来的,越界写是抽查,可能会检查出来
int a[10];
//a[15]=1;--err
//对于string而言,越界读和写都会报错
bit::string s1("hello");
//s1[6]='x';--err
//输入123输出123--不会输出hello world123
//std::string s1("hello world");
//cin >> s1;
//cout << s1 << endl;
/*std::string s2("hello world");
s2.resize(20);
s2[19] = 'x';
cout << s2 << endl;
cout << s2.c_str() << endl;*/
return 0;
}
比较
// s1 > s2
// "abc" "aa"
// "aa" "abc"
// "abc" "abc"
// "abcd" "abc"
// "abc" "abcd"
bool operator>(const string& s1, const string& s2)
{
size_t i1 = 0, i2 = 0;
while (i1 < s1.size() && i2 < s2.size())
{
if (s1[i1] > s2[i2])
{
return true;
}
else if (s1[i1] < s2[i2])
{
return false;
}
else
{
++i1;
++i2;
}
}
// "abc" "abc" false
// "abcd" "abc" true
// "abc" "abcd" false
if (i1 == s1.size())
{
return false;
}
else
{
return true;
}
}
bool operator==(const string& s1, const string& s2)
{
size_t i1 = 0, i2 = 0;
while (i1 < s1.size() && i2 < s2.size())
{
if (s1[i1] != s2[i2])
{
return false;
}
else
{
++i1;
++i2;
}
}
if (i1 == s1.size() && i2 == s2.size())
{
return true;
}
else
{
return false;
}
}
inline bool operator!=(const string& s1, const string& s2)
{
return !(s1 == s2);
}
inline bool operator>=(const string& s1, const string& s2)
{
return s1 > s2 || s1 == s2;
}
inline bool operator<(const string& s1, const string& s2)
{
return !(s1 >= s2);
}
inline bool operator<=(const string& s1, const string& s2)
{
return !(s1 > s2);
}
+
public里面的函数
// 存在深拷贝对象,尽量少用
string operator+(const string& s1, char ch)
{
string ret = s1;
ret += ch;
return ret;
}
string operator+(const string& s1, const char* str)
{
string ret = s1;
ret += str;
return ret;
}
fine
public里面的函数
//查找
size_t find(char ch, size_t pos = 0)
{
for (size_t i = pos; i < _size; ++i)
{
if (_str[i] == ch)
{
return i;
}
}
//npos表示没找到--无符号没有这么大的数
return npos;
}
size_t find(const char*sub, size_t pos = 0)
{
const char* p= strstr(_str + pos, sub);//匹配字符串
if (p == nullptr)
{
return npos;
}
else
{
return p-_str ;//指针-指针得到下标
}
return npos;
}