string类的成员变量分别是存储字符串的一段空间_str,表示字符串的有效字符个数_size和表示存储有效字符空间的_capacity。
private:
char *_str;
size_t _size;// 有效字符的个数
size_t _capacity;// 存储有效字符的空间
还有一个string类的特殊成员,npos表示size_t的最大值,一般表示表示string的结束位子。
private:
char* _str;
size_t _size;
size_t _capacity;
public:
const static size_t npos;
};
const size_t string::npos = -1;
-1也可以表示size_t类型的最大值。
注意:
首先,string类的有参构造函数其实可以设计为全缺省函数,缺省值设置为空串,当没有传入参数时使用缺省值,将_str设置为空串,这样就可以不需要定义无参构造函数了。其次,如果传入了参数,就将_size和_capacity设置为形参的长度,然后开一段大小和形参相同的空间给_str,最后将值拷贝过去即可。
//构造函数
string(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
//cout << "构造函数" << endl;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
string的拷贝构造函数,关于string的拷贝构造函数,我的建议是用传入的string类型中的_str去构造一个临时变量,然后交换临时变量和当前类内部的成员变量的内容。
void swap(string& s) {
std::swap(s._str, _str);
std::swap(s._size, _size);
std::swap(s._capacity, _capacity);
}
//拷贝构造
string(const string& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
//cout << "拷贝构造函数" << endl;
string tmp(s._str);
swap(tmp);
}
没啥好说的,直接交换右值即可。
//移动构造
string(string&& s) noexcept{
//cout << "移动构造函数" << endl;
swap(s);
}
默认重载的赋值运算符功能很简单,就是将原有对象的所有成员变量一一赋值给新对象,并且返回新对象,这和默认拷贝构造函数的功能类似。
因此其代码和拷贝构造函数代码差不多,只不过不用初始化,因为在构造时已经初始化完毕。
//拷贝赋值
string& operator=(const string& s) {
//cout << "拷贝赋值函数" << endl;
string tmp(s);
swap(tmp);
return *this;
}
直接交换资源就好。
//移动赋值
string& operator=(string&& s) noexcept {
//cout << "移动赋值函数" << endl;
swap(s);
return *this;
}
把_str空间释放,置空,并且把_size和_capacity 置0
//析构函数
~string() {
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
顾名思义返回字符串的长度(以字符数为单位)
size_t size() const {
return _size;
}
返回当前为 string 分配的存储空间的大小,以字符表示。
size_t capacity() const {
return _capacity;
}size_t capacity() const {
return _capacity;
}
表示请求更改容量(_capacity),使字符串容量适应计划的大小更改为最多 n 个字符。
注意是有效字符,不包含标识字符,而在具体实现的时候,我们在底层多开一个空间给\0。
这里我们的做法是,如果n >_capacity,那么我们就开始扩容, 先 创建一个新数组,把其大小扩容为n+1,留一个空间给 \0 ,然后将原本的 str中的内容拷贝进新数组,然后delete str开的空间,最后将str指向新空间,更新_capacity的内容。
代码如下:
void reserve(size_t n) {
if (n > _capacity) {
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
其实它的情况大体上可以分为插入数据和删除数据两种情况。
1.对于插入数据:直接调用【reserve】提前预留好空间,然后搞一个for循环将字符ch尾插到数组里面去,最后再在数组末尾插入一个\0标识字符;
2.对于删除数据:如果 n 小于当前字符串长度,则当前值将缩短为 n ,删除第 n 个字符以后的字符,然后重置一下_size的大小为n即可。
代码如下:
void resize(size_t n, char ch = '\0') {
if (_size > n) {
_str[n] = '\0';
_size = n;
}
else{
reserve(n);
while (_size < n) {
_str[_size] = ch;
++_size;
}
_str[n] = '\0';
}
}
顾名思义就是清除字符串,擦除string的内容,该内容变为空字符串(长度为 0 个字符)。
void clear(){
_str[0] = '\0';
_size = 0;
}
元素访问操作相对来说用的最多的就是operator[] .
对它进行调用时可能进行的是写操作,也可能进行读操作,所以为了适应const和非const对象,operator[]应该实现两个版本的函数,并且这个函数处理越界访问的态度就是assert直接断言,而at对于越界访问的态度是抛异常。
char& operator[](size_t pos) {
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos) const {
assert(pos < _size);
return _str[pos];
}
查找字符串中的第一个匹配项, 在string中搜索由其参数指定的序列的第一个匹配项。
直接从pos位置遍历即可。
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(sub + pos, sub);
if (p) {
return p - _str;
}
else {
return npos;
}
}
在字符串尾部插入单个数据。
首先,当我们的_size ==_capacity 的时候,我们要准备扩容,但是这里我们该如何扩容呢?
我的建议是,如果是初次扩容的时候,建议扩为四个字节,但以后我们每次都扩容为二倍,然后再_size的位置插入一个数据即可,注意补'\0',
在后续完成了insert的时候,也可以复用insert函数。
代码如下:
void push_back(char ch) {
if (_size == _capacity) {
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_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;
}
追加到字符串,通过在当前值的末尾附加其他字符来扩展
在这里我们只实现添加字符和字符串的操作。
我们可以直接复用【push_back】和 【append] 的操作来实现。
代码如下:
string& operator+=(char ch) {
push_back(ch);
return *this;
}
string& operator+=(const char* str) {
append(str);
return *this;
}
插入到字符串中,在 pos(或 p)指示的字符之前将其他字符插入
assert
先断言,确保插入位置合法ch
插入到指定位置pos
之后。 // insert(0, 'x')
void insert(size_t pos, char ch){
assert(pos <= _size);
if (_size == _capacity) {
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
size_t end = _size + 1;
while (end > pos) {
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch;
_size++;
}
void insert(size_t pos, const char* str) {
assert(pos <= _size);
size_t len = strlen(str);
if (len + _size > _capacity) {
/*reserve(_capacity == 0 ? 4 : _capacity * 2);*/
reserve(_size + len);
}
size_t end = _size;
while (end >= pos&&end!=npos) {
_str[end + len] = _str[end];
--end;
}
strncpy(_str + pos, str, len);
_size += len;
}
注意:这里在头部插入时有可能end会等于-1 ,插入字符串时需要判断字符串长度,才能进行元素的移动和元素的写入。
意思很简单,就是从字符串中删除字符
对于删除,思路很简单,分为两种情况下的删除:
1.如果当前位置加上要删除的长度大于字符串的长度,即【 pos + len >= _size】,此时的意思即为删除pos之后的所有元素;
2.除了上述情况,就是在字符串内正常删除操作。我们只需利用strcpy来进行,将pos+len之后的字符串直接覆盖到pos位置,这样实际上就完成了删除的工作。
代码如下:
void erase(size_t pos, size_t len = npos) {
assert(pos < _size);
size_t end = pos+len;
if (len == npos || len + pos > _size) {
_str[pos] = '\0';
_size = pos;
}
else {
size_t begin = pos + len;
while (begin <= _size)
{
_str[begin - len] = _str[begin];
++begin;
}
_size -= len;
}
}
获取等效的 C 字符串,返回指向一个数组的指针,该数组包含以 null 结尾的字符序列(即 C 字符串),表示string对象的当前值
const char* c_str() const {
return _str;
}
直接上代码吧。
bool operator<(const string& s) const {
return strcmp(_str, s._str) < 0;
}
bool operator==(const string& s) const {
return strcmp(_str, s._str) == 0;
}
bool operator<=(const string& s) const {
return *this < s || *this == s;
}
bool operator>(const string& s) const {
return !(*this <= s);
}
bool operator>=(const string& s) const {
return !(*this < s);
}
bool operator!=(const string& s) const {
return !(*this == s);
}
从字符串中提取子字符串
//截取string中的一段
string substr(size_t pos, size_t len = npos) {
string s;
size_t end = pos + len;
if (len == npos || pos + len > _size) {
len = _size - pos;
end = _size;
}
s.reserve(len);
for (size_t i = pos; i < len; i++) {
s += _str[i];
}
return s;
}
将字符串插入流, 将符合 str 值的字符序列插入到 os 中。
ostream& operator<<(ostream& out, const string& s)
{
for (auto ch : s)
out << ch;
return out;
}
从流中提取字符串, 从输入流中提取字符串,将序列存储在 str 中,该序列被覆盖(替换 str 的先前值)
istream& operator>>(istream& in, string& s)
{
s.clear();
char buff[129]{};
size_t i = 0;
char ch;
ch = in.get();
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 128)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
using namespace std;
namespace My {
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin(){
return _str;
}
iterator end(){
return _str + _size;
}
const_iterator begin() const{
return _str;
}
const_iterator end() const{
return _str + _size;
}
//构造函数
string(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
//cout << "构造函数" << endl;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
void swap(string& s) {
std::swap(s._str, _str);
std::swap(s._size, _size);
std::swap(s._capacity, _capacity);
}
//拷贝构造
string(const string& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
//cout << "拷贝构造函数" << endl;
string tmp(s._str);
swap(tmp);
}
//拷贝赋值
string& operator=(const string& s) {
//cout << "拷贝赋值函数" << endl;
string tmp(s);
swap(tmp);
return *this;
}
//移动构造
string(string&& s) noexcept{
//cout << "移动构造函数" << endl;
swap(s);
}
//移动赋值
string& operator=(string&& s) noexcept {
//cout << "移动赋值函数" << endl;
swap(s);
return *this;
}
//析构函数
~string() {
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
char& operator[](size_t pos) {
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos) const {
assert(pos < _size);
return _str[pos];
}
size_t capacity() const {
return _capacity;
}
size_t size() const {
return _size;
}
const char* c_str() const {
return _str;
}
void reserve(size_t n) {
if (n > _capacity) {
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void resize(size_t n, char ch = '\0') {
if (_size > n) {
_str[n] = '\0';
_size = n;
}
else{
reserve(n);
while (_size < n) {
_str[_size] = ch;
++_size;
}
_str[n] = '\0';
}
}
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(sub + pos, sub);
if (p) {
return p - _str;
}
else {
return npos;
}
}
//截取string中的一段
string substr(size_t pos, size_t len = npos) {
string s;
size_t end = pos + len;
if (len == npos || pos + len > _size) {
len = _size - pos;
end = _size;
}
s.reserve(len);
for (size_t i = pos; i < len; i++) {
s += _str[i];
}
return s;
}
void push_back(char ch) {
if (_size == _capacity) {
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_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) {
push_back(ch);
return *this;
}
string& operator+=(const char* str) {
append(str);
return *this;
}
// insert(0, 'x')
void insert(size_t pos, char ch){
assert(pos <= _size);
if (_size == _capacity) {
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
size_t end = _size + 1;
while (end > pos) {
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch;
_size++;
}
void insert(size_t pos, const char* str) {
assert(pos <= _size);
size_t len = strlen(str);
if (len + _size > _capacity) {
/*reserve(_capacity == 0 ? 4 : _capacity * 2);*/
reserve(_size + len);
}
size_t end = _size;
while (end >= pos&&end!=npos) {
_str[end + len] = _str[end];
--end;
}
strncpy(_str + pos, str, len);
_size += len;
}
void erase(size_t pos, size_t len = npos) {
assert(pos < _size);
size_t end = pos+len;
if (len == npos || len + pos > _size) {
_str[pos] = '\0';
_size = pos;
}
else {
size_t begin = pos + len;
while (begin <= _size)
{
_str[begin - len] = _str[begin];
++begin;
}
_size -= len;
}
}
bool operator<(const string& s) const {
return strcmp(_str, s._str) < 0;
}
bool operator==(const string& s) const {
return strcmp(_str, s._str) == 0;
}
bool operator<=(const string& s) const {
return *this < s || *this == s;
}
bool operator>(const string& s) const {
return !(*this <= s);
}
bool operator>=(const string& s) const {
return !(*this < s);
}
bool operator!=(const string& s) const {
return !(*this == s);
}
void clear(){
_str[0] = '\0';
_size = 0;
}
private:
char* _str;
size_t _size;
size_t _capacity;
public:
const static size_t npos;
};
const size_t string::npos = -1;
ostream& operator<<(ostream& out, const string& s)
{
for (auto ch : s)
out << ch;
return out;
}
istream& operator>>(istream& in, string& s)
{
s.clear();
char buff[129]{};
size_t i = 0;
char ch;
ch = in.get();
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 128)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
void test_string1()
{
string s1("hello world");
cout << s1.c_str() << endl;
string s2;
cout << s2.c_str() << endl;
for (size_t i = 0; i < s1.size(); i++)
{
cout << s1[i] << " ";
}
cout << endl;
string::iterator it = s1.begin();
while (it != s1.end())
{
(*it)++;
cout << *it << " ";
++it;
}
cout << endl;
for (auto& ch : s1)
{
ch++;
cout << ch << " ";
}
cout << endl;
cout << s1.c_str() << endl;
}
void test_string2()
{
string s1("hello world");
cout << s1.c_str() << endl;
s1.push_back(' ');
s1.append("hello bit hello bit");
cout << s1.c_str() << endl;
s1 += '#';
s1 += "*********************";
cout << s1.c_str() << endl;
string s2;
s2 += '#';
s2 += "*********************";
cout << s2.c_str() << endl;
}
void test_string3()
{
string s1("hello world");
cout << s1.c_str() << endl;
s1.insert(5, '%');
cout << s1.c_str() << endl;
s1.insert(s1.size(), '%');
cout << s1.c_str() << endl;
s1.insert(0, '%');
cout << s1.c_str() << endl;
}
void test_string4()
{
string s1("hello world");
string s2("hello world");
cout << (s1 >= s2) << endl;
s1[0] = 'z';
cout << (s1 < s2) << endl;
cout << s1 << endl;
cin >> s1;
cout << s1 << endl;
/*char ch1, ch2;
cin >> ch1 >> ch2;*/
}
void test_string5()
{
string s1("hello world");
s1.insert(5, "abc");
cout << s1 << endl;
s1.insert(0, "xxx");
cout << s1 << endl;
s1.erase(0, 3);
cout << s1 << endl;
s1.erase(5, 100);
cout << s1 << endl;
s1.erase(2);
cout << s1 << endl;
}
void test_string6()
{
string s1("hello world");
cout << s1 << endl;
s1.resize(5);
cout << s1 << endl;
s1.resize(25, 'x');
cout << s1 << endl;
}
void test_string7()
{
string s1("test.cpp.tar.zip");
//size_t i = s1.find('.');
//size_t i = s1.rfind('.');
//string s2 = s1.substr(i);
//cout << s2 << endl;
string s3("https://legacy.cplusplus.com/reference/string/string/rfind/");
//string s3("ftp://www.baidu.com/?tn=65081411_1_oem_dg");
// 协议
// 域名
// 资源名
string sub1, sub2, sub3;
size_t i1 = s3.find(':');
if (i1 != string::npos)
sub1 = s3.substr(0, i1);
else
cout << "没有找到i1" << endl;
size_t i2 = s3.find('/', i1 + 3);
if (i2 != string::npos)
sub2 = s3.substr(i1 + 3, i2 - (i1 + 3));
else
cout << "没有找到i2" << endl;
sub3 = s3.substr(i2 + 1);
cout << sub1 << endl;
cout << sub2 << endl;
cout << sub3 << endl;
}
void test_string8()
{
string s1("hello world");
string s2 = s1;
cout << s1 << endl;
cout << s2 << endl;
string s3("xxxxxxxxxxxxxxxxxxx");
s2 = s3;
cout << s2 << endl;
cout << s3 << endl;
}
void test_string9()
{
string s1("hello world");
cin >> s1;
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
}
}