什么是string类

string类是是表示字符串的字符串类

该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
不能操作多字节或者变长字符的序列。

学习使用string(常用的接口)

我们在使用的时候我们包一下他的头文件#include<string>

1、string构造函数

我们定义对象构造函数有很多种形式。

int main()
{string s1;cout << "s1="<<s1 << endl;string s2("hello world");cout << "s2=" << s2 << endl;string s3 = "hello world";cout << "s3=" << s3 << endl;//表示从s3,取第6个位置一共三个位置进行初始化。也就是worstring s4 (s3, 6, 3);cout << "s4=" << s4 << endl;//字符串太短,就直接取到尾string s5 (s3, 6, 13);cout << "s5=" << s5 << endl;string s6(s3, 6);cout << "s6=" << s6 << endl;string s7("hello world", 5);cout << "s7=" << s7 << endl;string s8(10, '*');cout << "s8=" << s8 << endl;return 0;
}

2、 string类对象的容量操作

sizelength两个功能相同,都是计算字符串的长度。
我们用size就行

max_size就是能存放最大的量。但是实际存不了。
capacity,就是容量。但是计算的时不包括\0的位置。

int main()
{string s1("hello world");cout << s1.size() << endl;cout << s1.length() << endl;cout << s1.max_size() << endl;out << s1.capacity() << endl;}

reserve是提前开空间,如果我们知道需要多少空间,提前开空间按,减少扩容,提高效率。
并且reserve,只会改变capacity并不会改变size
resize:不仅可以开空间,而且还可以将其初始化,它会改变capacity也会改变size。将size变小,就可以删除数据

int main()
{string s1;s1.reserve(100);cout << s1.size() << endl;cout << s1.capacity() << endl;string s2;//用缺省参数,将其初始化为\0//s2.resize(100);//将其前100个初始化为xs2.resize(100, 'x');cout << s2.size() << endl;cout << s2.capacity() << endl;
}

3、string类对象的访问及遍历操作

int main()
{string s1("hello");s1.push_back(' ');s1.push_back('!');cout << s1 << endl;s1.append("world");cout << s1 << endl;}
//两种操作都一样
int main()
{//用运算符重载+=什么都可以用。//既可以添加字符,也可以添加字符串string s1("hello");s1 += ' ';s1 += '!';cout << s1 << endl;s1 += "world";cout << s1 << endl;
}

4、迭代器

我们可以想像迭代器就是像指针一样的东西。
可以模仿快排的双指针
begin()表示字符的第一个指针。
end()表示字符最后一个指针的下一个指针。
相当于一个左闭右开的区间。

正向迭代器

int main()
{string s1("hello world");string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";++it;}cout << endl;return 0;
}

反向迭代器

既然我们支持正向遍历数组,那么我们也支持反向遍历数组。
就是所谓的反向迭代器.
反向迭代器reverde_iterator

int main()
{string s1("hello world");//rbegin指向\0,rend指向第一个字符的前一个string::reverse_iterator it = s1.rbegin();while (it != s1.rend()){cout << *it << " ";++it;}cout << endl;return 0;
}

正向const迭代器

int main()
{const string s1("hello world");//只允许遍历和读容器的数据,不能写//类比指针,const修饰的是*it,但是it可以修改。string::const_iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";++it;}cout << endl;return 0;
}

反向const迭代器

int main()
{const string s1("hello world");//rbegin指向\0,rend指向第一个字符的前一个string::const_reverse_iterator it = s1.rbegin();while (it != s1.rend()){cout << *it << " ";++it;}cout << endl;return 0;
}

我们发现反向const迭代器怎么这么长,每回写都让我们很烦,这时我们的auto就可以上场了。可以自动识别类型,但是对可读性造成一定的影响。所以我们必须要在足够了解的情况下使用。

int main()
{const string s1("hello world");//rbegin指向\0,rend指向第一个字符的前一个//string::const_reverse_iterator it = s1.rbegin();auto it = s1.rbegin();while (it != s1.rend()){cout << *it << " ";++it;}cout << endl;return 0;
}

5、访问string的三种方式

第一种:用下标的方式访问字符。
第二种:范围for,其实他的本质就是迭代器
第三种:迭代器

6、insert和erase

在指定位置插入或删除。

7、operate []at

两个功能其实是一样的

8. replacefindrfind

replace将指定位置替换为自己想要的
find找到自己想要找到的位置,找到返回下标,没找到返回npos,也就是-1
rfind从后往前找到自己想要找到的位置,找到返回下标,没找到返回npos,也就是-1

面试题:将这个字符串的空格替换为%%20.
`“hello world I love ereryday!”

int main()
{string s1 = "hello world I love you!";//我们提前算好我们的空格,然后提前开空间size_t num = 0;for (auto ch : s1){if (ch == ' '){++num;}}//提前开空间,避免replace时扩容s1.reserve(s1.size() + 3 * num);//第二个参数是缺省值=0,就是从开始的地方找size_t pos = s1.find(' ');while (string::npos != pos){s1.replace(pos, 1, "%%20");//在pos之后第四个位置开始找pos = s1.find(' ',pos+4);}cout << s1 << endl;
}

`

//第二种方法以空间换时间
int main()
{string s1 = "hello world I love you!";//我们提前算好我们的空格,然后提前开空间string tmp;for (auto ch : s1){if (ch != ' '){tmp += ch;}if (ch == ' '){tmp += "%% 20";}}s1 = tmp;cout << s1 << endl;
}

9 、string中的swap

在我们的string中我们也有一个交换函数。库里面也有一个swap。
我们使用string中swap效率更高。

因为在我们的string中可以直接交换指针,指向不同的字符串。
而库中的swap,我们还要用一个临时变量去交换。

int main()
{string s1("abcd");string s2("xxxx");//用string中的交换s1.swap(s2);cout << s1 << endl << s2 << endl;//用库中的交换swap(s1, s2);cout << s1 << endl << s2 << endl;
}

10、c_str

c_str返回字符串的地址

int main()
{string s1("hello world");cout << s1 << endl;cout << s1.c_str() << endl;
}

第一个我们是用的进行运算符重载后的流插入,按照我们的size打印,size多大就打印多少。
第二个是用的库里的流插入。s1.c_str()相当于一个字符串指针。
我们看似没有差别,其实差别很大。
看下面这个例子

int main()
{string s1("hello world");s1 += '\0';s1 += '\0';s1 += "xxxxx";cout << s1 << endl;cout << s1.c_str() << endl;
}


用c_str的例子

int main()
{string fliename("test.cpp");FILE* fout = fopen(fliename.c_str(), "r");char ch = fgetc(fout);while (ch != EOF){cout << ch;ch = fgetc(fout);}return 0;
}

11、substr

显示子串

int main()
{string file("string.cpp");size_t pos = file.find('.');//取一个子串//后面那个参数直接用缺省值,直接就可以取到结尾string suffix = file.substr(pos);cout << suffix;
}

12、find_first_of

在字符串中搜索与其参数中指定的任何字符匹配的每个字符。

例题
"please,look me"中的abe替换字符为x.

int main()
{string s1("please ,look me");size_t found = s1.find_first_of("abe");while (found != string::npos){s1[found] = 'x';found = s1.find_first_of("abe");}cout << s1;
}

13、getline

我们的cin输入,遇到空格换行就结束了。
我们如果像输入一句话,对于cin只能输入一个单词。

但是我们getline遇到换行才会结束。
string s1; getline(cin,s1);

例题字符串最后一个单词的长度

模拟实现string

首先,我们模拟实现string,包括三个成员变量(字符指针,大小,容量)
为了不和库里的string冲突,我们需要进行一步操作。
将我们模拟实现的string封装起来。具体怎么做看下面代码

//外面这个类将string包装起来,防止冲突
namespace tongtong
{//模拟实现的stringclass string{public:private:char* _str;size_t size;size_t capacity;};
};

一、string的构造函数

①无参的构造函数

string():_str(nullptr), _size(0),_capacity(0)
{}

其实对于我们的无参构造函数看着没什么问题,其实有点问题。那我举一个例子大家看看什么问题。
我们上面了解了c_str,我们如果要实现这个要求我们然后这个字符串的地址。

const char* c_str()
{return _str;
}

我们如果想打印我们的字符串,就会发现直接报错,那么出现了什么问题呢?

大家先思考一下
我们s1进行无参的构造函数,并且在初始化列表中初始化了空指针。我们先通过s1找到了c_str的成员函数,这时候是没有崩溃的,并且将空指针返回也是没有问题的。问题就出在我们通过这个空指针打印。这时就涉及到了空指针的解引用。就会出现问题。

②代参的构造函数

string(const char* str):_str(str),_size(strlen(str)),_capacity(strlen(str))
{}

其实他也是有问题的
我们设置成带参数,str的类型是const char*,但是我们成员变量_str是char*类型,我们在初始化列表中传参数的时候就涉及到权限的放大。
所以我们的解决办法就是将我们的成员参数也设置为const。
这样的操作无疑是局限的,那么我们后续的操作就都不可以改变我们的_str。

③修改构造函数

我们先改我们带参数的构造函数,我们的直接将我们的常量字符串给他,肯定不可以,那么我们就自己开一块空间,将字符串的内容拷贝过来。这样我们自己开辟的空间就可以自己随意修改,而不是像我们上面将成员变量修改为const。

我们开辟空间,就没有必要在初始化列表开空间,就直接在函数中开就可以。具体的细节我们开代码注释。

string(const char* str):_size(strlen(str)){//为什们将capacity的初始化也放到函数中?//在初始化列表,初始化的顺序跟定义的顺序有关,//如果我们先定义的capacity,我们size还没定义就是随机值。_capacity = _size;//因为capacity不包括字符的结束标准 \0 //所以在开辟空间的时候,我们多开一位。_str = new char [_capacity + 1];//将我们字符串的内容拷贝过来。strcpy(_str, str);}

修改无参数的构造函数,我们防止空指针的解引用,我们可以也给他开一个字节的空间,放\0,也就是字符串的结束标志,表示空字符串。

     string()//我们初始化为什们要用new[]呢?//为了跟我们带参构造函数保持一致,//在析构的时候用一样的类型。delete[]new;:_str(new char[1]{'\0'}), _size(0),_capacity(0){}

我们在类和对象中,我们知道,无参的构造函数可以用缺省参数代替,所以我们将带参数的构造函数用缺省参数,就可以将这两个合二为一。

//下面两种形式都可以,我们""里面就有\0。
//string(const char* str = "\0")
string(const char* str = ""):_size(strlen(str))
{_capacity = _size == 0 ? 3 : _size;_str = new char [_capacity + 1];strcpy(_str, str);
}

二、析构函数

很简单不想解释了,直接看代码

~string()
{//注意的点就是,delete一定要和new保持一致delete[]_str;_str = nullptr;_size = _capacity = 0;
}

三、拷贝构造

编译器会自动生成我们的拷贝构造,但是我们在类和队对象的章节学过,它完成的是浅拷贝。
对于我们的字符串,我们_str是一个指针,指向一块数组,当我们完成浅拷贝的时候,我们拷贝出来的指针只是将_str的四个字节拷贝了过去,所以两块空间用指针指向同一块空间。

当我们进行析构函数的时候,我们同一块空间析构了两次,这显然是由问题的,所以会直接报错,我们不能直接用默认拷贝构造,我们要自己写。

//拷贝构造
string(const string& s): _size(s._size), _capacity( s._capacity)
{//防止new失败,我们先创建一个临时变量开辟空间//拷贝完后,在给_strchar* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);_str = tmp;
}

四、赋值重载

这个也是默认成员函数,编译器会自动生成,跟拷贝构造一样进行浅拷贝,和我们上面出现的情况一样,所以我们要进行深拷贝,自己写一个赋值重载。

//赋值重载
string& operator=(string& s)
{//防止自己和自己赋值if (this != &s){// 防止new失败,我们先创建一个临时变量开辟空间//拷贝完后,在给_strchar* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[]_str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;}

五、返回sizecapacity

就是将我们的size直接返回,需要注意的一点就是我们返回size,一般都是不可修改的,不可修改一般就设置成const
这样的话,就跟加安全。

size_t size() const
{return _size;
}
size_t capacity() const
{return _size;
}

六、[]的运算符重载

这个也是比较容易,就是返回字符串的位置,我们可以通过引用返回改变我们字符串的值。
但是当我们想要打印字符串的时候,我们并不想修改字符串,我们就可以用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];
}

七、迭代器

我们暂时将其考虑为指针,就可以。
像用指针一样,用迭代器。我们先写一个正向迭代器。

① 正向迭代器。

//迭代器,//我们先知道怎么用就可以
typedef char* itterator;
itterator begin()
{return _str;
}
itterator end()
{return _str + _size;
}

用迭代器遍历字符串。

//测试迭代器
void test_string4()
{string s1("hello world");string::itterator it = s1.begin();while (it != s1.end()){cout << *it << " ";it++;}cout << endl;//范围for的底层就是迭代器for (auto ch : s1){cout << ch << " ";}cout << endl;
}

② 正向const迭代器

我们再来一个正向const迭代器

//正向const迭代器
typedef const char* const_itterator;
const_itterator begin() const
{return _str;
}
const_itterator end() const
{return _str + _size;
}

八、string比较大小(运算符重载)

这个唯一注意的点就是
我们如果不修改就加上const

//比较大小
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 ||  s == * this ;
}
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);
}

八、扩容

扩容我们知道有两种方式,
一种是reserve,改变capacity不变size
另一种是resize,即变capacity,还变size,还能初始化

reserve扩容

void reserve(size_t n)
{//reserve 不支持缩容//所以我们自己加个条件if (n > _capacity){//给\0开一个空间char* tmp = new char[n + 1];strcpy(tmp, _str);delete[]_str;_str = tmp;_capacity = n;}
}

resize扩容

//扩容
//resize是扩容+初始化,用缺省参数就是将其初始化
void resize(size_t n, char ch = '\0')
{//也不支持缩容但是会改sz的大小if (n < _size){//删除数据,保留前n个_size = n;_str[_size] = '\0';}else if(n > _size){if (n < _capacity){memset(_str+_size, ch, n - _size );_size = n;_str[_size] = '\0';}else{reserve(n);memset(_str+_size, ch, n - _size );_size = n;_str[_size] = '\0';}}}

九、尾插数据

增加数据有两种方式,
一种是push_back,增加一个字符
另一种是append增加一个字符串

push_back

//增加数据
//增加一个字符
void push_back(char ch)
{if (_size + 1 > _capacity){//二倍扩容reverse(_capacity * 2);}_str[_size] = ch;++_size;//最后加上\0_str[_size] = '\0';
}

append

//增加一个字符串
void append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){//如果原数组太小,二倍之后还可能放不下reverse(_size + len);}strcpy(_str + _size, str);_size += len;//最后加上\0_str[_size] = '\0';

十、+=的运算符重载(替换尾插数据的函数)

我们直接用+=既简单又好看。
就是利用上面的两个函数,构成运算符重载

string& operator+=(char ch)
{push_back(ch);return *this;
}
string& operator+=(const char* str)
{append(str);return *this;
}

十一、在固定为插入,删除

这时我们应该用insert
在我们的插入也分为插入一个字符,和插入一个字符串。

①插入一个字符

//插入数据
//在某个位置插入一个字符
void insert(size_t pos, char ch)
{assert(pos <= _size);if (_size + 1 > _capacity){reserve(_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 (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (end > pos + len - 1 ){_str[end] = _str[end - len];end--;}strncpy(_str + pos, str, len);_size += len;
}

③ 删除

//删除数据
void erase(size_t pos, size_t len = npos)
{if (len == npos || len >= _size - pos){_str[pos] = '\0';}else{strcpy(_str + pos, _str + pos + len);_size -= pos;}
}

十二、交换

将我们两个指针进行交换。
所以比我们库里的交换函数快,少了一次拷贝构造,和两次赋值重载。

//交换
void swap(string& s)
{std::swap(_str, s._str);std::swap(_capacity, s._capacity);std::swap(_size, s._size);
}

十三、find

就是在指定位置找。但是查找有两种方式
一种是查找字符
第二种是查找子串

① 找字符

size_t find(char ch, int pos = 0)
{assert(pos <= _size);for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;
}

②找子串

//查找一个子串
size_t find(const char* str, size_t pos = 0)
{assert(pos < _size);char * p = strstr(_str + pos, str);if (p == nullptr){return npos;}else{//指针相减就是中间的个数return p - _str;}
}

十四、流插入,流提取

流插入

我们的流插入不一定就是友元函数,我们也可以用迭代器,和[]来找到他的数据

//流插入
ostream& operator<<(ostream& out, const string s)
{for (int i = 0; i < s.size(); i++){out << s[i];}return out;
}

流提取

我们看下面这个流提取,并分析他为什么是错的

istream& operator>>(istream& in, string s)
{s.clear();char ch;in >> ch;while (ch != ' ' && ch != '\n'){s += ch;in >> ch;}return in;
}

cin的缓冲区,输入多个数据,两个数据分割就是空格或者换行,编译器认为我们是要多个字符,所以我们的空格换行我们就拿不到,它一直在读数据。所以cinscanf识别不了' ''\n'

所以我们可以提取数据需要不区分空格的操作符。
正好C++种get就不会区分,每一个字符我们都可以拿到。

istream& operator>>(istream& in, string s)
{//每次提取都将我们的空间为空s.clear();char ch = in.get();//防止输入的字符串太长,一直扩容char buff[128];size_t i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[127] = '\0';s += buff;i = 0;}ch = in.get();}if (i != 0){buff[i] = '\0';s += buff;}return in;
}

模拟实现的全部代码

string.h

#pragma once
#include<assert.h>
//外面这个类将string包装起来,防止冲突
namespace tongtong
{//模拟实现的stringclass string{public://string()//  //我们初始化为什们要用new[]呢?//    //为了跟我们带参构造函数保持一致,// //在析构的时候用一样的类型。delete[]new;//   :_str(new char[1]{'\0'})//    , _size(0)//    ,_capacity(0)//{//}//string(const char* str)//  :_size(strlen(str))//{//    //为什们将capacity的初始化也放到函数中?//  //在初始化列表,初始化的顺序跟定义的顺序有关,//    //如果我们先定义的capacity,我们size还没定义就是随机值。//    _capacity = _size;//   //因为capacity不包括字符的结束标准 \0 //    //所以在开辟空间的时候,我们多开一位。//   _str = new char [_capacity + 1];//    //将我们字符串的内容拷贝过来。//  strcpy(_str, str);//}//下面两种形式都可以,我们""里面就有\0。//string(const char* str = "\0")string(const char* str = ""):_size(strlen(str)){_capacity = _size == 0 ? 3 : _size;_str = new char [_capacity + 1];strcpy(_str, str);}//拷贝构造string(const string& s): _size(s._size), _capacity( s._capacity){//防止new失败,我们先创建一个临时变量开辟空间//拷贝完后,在给_strchar* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);_str = tmp;}//赋值重载string& operator=(string& s){//防止自己和自己赋值if (this != &s){// 防止new失败,我们先创建一个临时变量开辟空间//拷贝完后,在给_strchar* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[]_str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;}size_t size() const{return _size;}size_t capacity() const{return _capacity;}const char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}//正向迭代器typedef char* itterator;itterator begin(){return _str;}itterator end(){return _str + _size;}//正向const迭代器typedef const char* const_itterator;const_itterator begin() const{return _str;}const_itterator end() const{return _str + _size;}//比较大小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 ||  s == * this ;}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 reserve(size_t n){//reserve 不支持缩容//所以我们自己加个条件if (n > _capacity){//给\0开一个空间char* tmp = new char[n + 1];strcpy(tmp, _str);delete[]_str;_str = tmp;_capacity = n;}}//扩容//resize是扩容+初始化,用缺省参数就是将其初始化void resize(size_t n, char ch = '\0'){//也不支持缩容但是会改sz的大小if (n < _size){//删除数据,保留前n个_size = n;_str[_size] = '\0';}else if(n > _size){if (n < _capacity){memset(_str+_size, ch, n - _size );_size = n;_str[_size] = '\0';}else{reserve(n);memset(_str+_size, ch, n - _size );_size = n;_str[_size] = '\0';}}}//增加数据//增加一个字符void push_back(char ch){if (_size + 1 > _capacity){//二倍扩容reserve(_capacity * 2);}_str[_size] = ch;++_size;//最后放上\0_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;//最后加上\0_str[_size] = '\0';}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}//插入数据//在某个位置插入一个字符string& insert(size_t pos, char ch){assert(pos <= _size);if (_size + 1 > _capacity){reserve(_capacity * 2);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;return *this;}//在某个位置插入一个字符串string& insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (end > pos + len - 1 ){_str[end] = _str[end - len];end--;}strncpy(_str + pos, str, len);_size += len;return *this;}//删除数据string& erase(size_t pos, size_t len = npos){if (len == npos || len >= _size - pos){_str[pos] = '\0';}else{strcpy(_str + pos, _str + pos + len);_size -= pos;}return *this;}//交换void swap(string& s){std::swap(_str, s._str);std::swap(_capacity, s._capacity);std::swap(_size, s._size);}//查找一个字符size_t find(char ch, size_t pos = 0){assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}//查找一个子串size_t find(const char* str, size_t pos = 0){assert(pos < _size);char * p = strstr(_str + pos, str);if (p == nullptr){return npos;}else{//指针相减就是中间的个数return p - _str;}}//滞空字符串void clear(){_str[0] = '\0';_size = 0;}const char* c_str(){return _str;}~string(){delete[]_str;_str = nullptr;_size = _capacity = 0;}private:char* _str;size_t _size;size_t _capacity;//定义一个无符号的静态成员变量,当我们的默认参数static const size_t npos;};size_t const string::npos = -1;//流插入ostream& operator<<(ostream& out, const string s){for (int i = 0; i < s.size(); i++){out << s[i];}return out;}//istream& operator>>(istream& in, string s)//{//  char ch;//  in >> ch;//   while (ch != ' ' && ch != '\n')// {//     s += ch;//        in >> ch;//   }// return in;//}istream& operator>>(istream& in, string s){//每次提取都将我们的空间为空s.clear();char ch = in.get();//防止输入的字符串太长,一直扩容char buff[128];size_t i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[127] = '\0';s += buff;i = 0;}ch = in.get();}if (i != 0){buff[i] = '\0';s += buff;}return in;}void test_string1(){string s1;string s2("hello world");string::itterator it = s1.begin();while (it != s1.end()){cout << *it << endl;//it++;}cout << endl;cout << s1.c_str() << endl;cout << s2.c_str() << endl;s2[0]++;cout << s1.c_str() << endl;cout << s2.c_str() << endl;}void test_string2(){string s1;string s2("hello world");string s3(s2);string s4 = s2;cout << s3.c_str() << endl;cout << s4.c_str() << endl;s1 = s3;cout << s1.c_str() << endl;s1 = s1;cout << s1.c_str() << endl;}void Print(const string& s) {for (size_t i = 0; i < s.size(); i++){cout << s[i] << " ";}string::const_itterator it = s.begin();while (it != s.end()){cout << *it << " ";it++;}}void test_string3(){string s1("hello world");for (size_t i = 0; i < s1.size(); i++){s1[i]++;}Print(s1);cout << endl;}void test_string4(){string s1("hello world");string::itterator it = s1.begin();while (it != s1.end()){(*it)--;cout << *it << " ";it++;}cout << endl;//范围for的底层就是迭代器for (auto ch : s1){cout << ch << " ";}cout << endl;}void test_string5(){string s1("hello world");string s2("hello world");cout << (s1 < s2) << endl;cout << (s1 >= s2) << endl;cout << (s1 == s2) << endl;//s1.push_back(' ');//s1.append("hello");s1 += ' ';s1 += "hello";cout << s1.c_str() << endl;}void test_string6(){string s1("abcdef");//s1.resize(20, 'x');//cout << s1.c_str() << endl;//s1.resize(30, 'y');//cout << s1.c_str() << endl;//s1.resize(10);//cout << s1.c_str() << endl;s1.insert(0, 'x');cout << s1.c_str() << endl;s1.insert(0, "222222");cout << s1.c_str() << endl;}void test_string7(){string s1("abcdef");//s1 += '\0';s1 += "xxxx";cout << s1 << endl;cout << s1.c_str() << endl;/* string s2("xxxxxx");*///s1.swap(s2);//cout << s1.c_str() << endl;//cout << s2.c_str() << endl;cin >> s1;}
};

test.cpp

#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream>
using namespace std;
#include"string.h"int main()
{tongtong::test_string1();return 0;
}

STL的string功能以及模拟实现相关推荐

  1. 【C++】STL简介 -- string 的使用及其模拟实现

    文章目录 一.STL 简介 1.什么是 STL 2.STL 的版本 3.STL 的六大组件 4.STL 的重要性 5.如何学习 STL 二.string 类的使用 1.什么是 string 2.VS ...

  2. STL简介string的使用及其模拟实现

    文章目录 一.STL简介 1.什么是STL 2.STL的版本 3.STL的六大组件 4.STL的重要性 5.如何学习STL 二.标准库中的string类 1.string类 2.string类的常用接 ...

  3. STL之string类:知其然,需知其所以然

    目录 前言 一,构造及初始化 1.1constuct类函数 1.2构造函数及其模拟实现 1.3拷贝构造及其模拟实现 1.4赋值操作符 1.5string类的swap接口 二,迭代器 2.1初识迭代器即 ...

  4. STL 的string类怎么啦?

    STL 的string类怎么啦? 陈皓 前言 上个周末在和我的同学爬香山闲聊时,同学说到STL中的string类曾经让他备受折磨,几年前他开发一个系统前对string类还比较清楚,然后随着程序的复杂度 ...

  5. C++STL的string容器

    C++STL的string容器 string容器 string基本概念 string构造函数 string赋值操作 string字符串拼接 string查找和替换 string字符串比较 string ...

  6. String类的模拟实现

    目录: 一.经典String类的问题 1,浅拷贝 2,深拷贝 二.现代写法版的string类 三.传统String类的模拟实现 1.迭代器 2.operator[] 3.size() 4.c_str( ...

  7. C++关于string类的模拟实现

    文章目录 一.string类的模拟实现 1.成员变量 2.构造函数 (1)无参构造函数 (2)有参构造函数 3.c_str函数 4.operator[] 5.深浅拷贝问题 (1)浅拷贝 (2)深拷贝 ...

  8. Vehicle Spy3软件常用功能之模拟报文发送

    Vehicle Spy3软件常用功能之模拟报文发送 功能说明 在很多测试场景下,我们都需要通过往总线发送特定报文对控制器的功能进行测试,模拟报文发送的方式可以简单.直接地满足我们的需求. 模拟报文发送 ...

  9. 6-2-2:STL之string——string的模拟实现

    文章目录 实现 (1)构造和析构 (2)拷贝构造-深浅拷贝问题 A:默认拷贝构造的问题(浅拷贝) B:深拷贝 (3)赋值重载 (4)迭代器 (5)push_back,append和+= 代码 实现 ( ...

最新文章

  1. CTF-密码学-攻防世界-幂数加密(云影加密)解密脚本
  2. 五行代码快速使用python的turtle库绘画
  3. vue中mixin的一点理解
  4. iframe中跨域页面访问parent的方法
  5. 图像局部显著性—点特征(FREAK)
  6. font awesome java_java awt实现 fontawesome转png
  7. 单例设计模式共享数据分析、解决,call_once
  8. ubuntu mysql 5.7 出错_ubuntu mysql5.7 启动提示错误:/var/run/mysqld/mysqld.sock
  9. linux打包解压工具,linux下的解压,打包工具
  10. Spotfire 将字符串日期 转换为 日期格式
  11. python猜数游戏续_python实现猜数游戏
  12. 计算机内存有何组成,电脑4个4g内存条组成16g内存和16g内存有什么不同?
  13. 使用LaTeX写数学公式
  14. python 斗地主发牌_tkinter模拟斗地主发牌
  15. C语言一些有趣的现象(例子) (译)
  16. 2.linux中tty、ttySn、ttyUSBn的区别及查看
  17. Java基础练习——吃货联盟
  18. 通讯方式:近场通讯和无线通讯
  19. 前端开发第三方分享/登录功能备忘(facebook、weibo、QQ、weixin微信好友或者朋友圈)
  20. JavaScript空判断

热门文章

  1. 跟Gregg学习系统性能--[7.4.5]内存性能指标监测内容
  2. Validform验证插件
  3. 搞计算机的离开高校的几个理由
  4. Unity Lighting参数和烘焙
  5. Android 启动页页面慢 解决方案
  6. 《被讨厌的勇气》节选:直面人生,课题分离
  7. matlab中sort(),atan2(y,x),normrnd(),boxplot()函数,cell数组用法学习
  8. android studio环境配置
  9. 早教:平板电脑的新妙用
  10. 张鑫溢:8.30今日黄金原油是涨还是跌,鲍威尔暗示打压金价走势?