STL的string功能以及模拟实现
什么是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
类对象的容量操作
size
、length
两个功能相同,都是计算字符串的长度。
我们用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. replace
和find
和rfind
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;}
五、返回size
和capacity
就是将我们的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
的缓冲区,输入多个数据,两个数据分割就是空格或者换行,编译器认为我们是要多个字符,所以我们的空格换行我们就拿不到,它一直在读数据。所以cin
和scanf
识别不了' '
和'\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功能以及模拟实现相关推荐
- 【C++】STL简介 -- string 的使用及其模拟实现
文章目录 一.STL 简介 1.什么是 STL 2.STL 的版本 3.STL 的六大组件 4.STL 的重要性 5.如何学习 STL 二.string 类的使用 1.什么是 string 2.VS ...
- STL简介string的使用及其模拟实现
文章目录 一.STL简介 1.什么是STL 2.STL的版本 3.STL的六大组件 4.STL的重要性 5.如何学习STL 二.标准库中的string类 1.string类 2.string类的常用接 ...
- STL之string类:知其然,需知其所以然
目录 前言 一,构造及初始化 1.1constuct类函数 1.2构造函数及其模拟实现 1.3拷贝构造及其模拟实现 1.4赋值操作符 1.5string类的swap接口 二,迭代器 2.1初识迭代器即 ...
- STL 的string类怎么啦?
STL 的string类怎么啦? 陈皓 前言 上个周末在和我的同学爬香山闲聊时,同学说到STL中的string类曾经让他备受折磨,几年前他开发一个系统前对string类还比较清楚,然后随着程序的复杂度 ...
- C++STL的string容器
C++STL的string容器 string容器 string基本概念 string构造函数 string赋值操作 string字符串拼接 string查找和替换 string字符串比较 string ...
- String类的模拟实现
目录: 一.经典String类的问题 1,浅拷贝 2,深拷贝 二.现代写法版的string类 三.传统String类的模拟实现 1.迭代器 2.operator[] 3.size() 4.c_str( ...
- C++关于string类的模拟实现
文章目录 一.string类的模拟实现 1.成员变量 2.构造函数 (1)无参构造函数 (2)有参构造函数 3.c_str函数 4.operator[] 5.深浅拷贝问题 (1)浅拷贝 (2)深拷贝 ...
- Vehicle Spy3软件常用功能之模拟报文发送
Vehicle Spy3软件常用功能之模拟报文发送 功能说明 在很多测试场景下,我们都需要通过往总线发送特定报文对控制器的功能进行测试,模拟报文发送的方式可以简单.直接地满足我们的需求. 模拟报文发送 ...
- 6-2-2:STL之string——string的模拟实现
文章目录 实现 (1)构造和析构 (2)拷贝构造-深浅拷贝问题 A:默认拷贝构造的问题(浅拷贝) B:深拷贝 (3)赋值重载 (4)迭代器 (5)push_back,append和+= 代码 实现 ( ...
最新文章
- CTF-密码学-攻防世界-幂数加密(云影加密)解密脚本
- 五行代码快速使用python的turtle库绘画
- vue中mixin的一点理解
- iframe中跨域页面访问parent的方法
- 图像局部显著性—点特征(FREAK)
- font awesome java_java awt实现 fontawesome转png
- 单例设计模式共享数据分析、解决,call_once
- ubuntu mysql 5.7 出错_ubuntu mysql5.7 启动提示错误:/var/run/mysqld/mysqld.sock
- linux打包解压工具,linux下的解压,打包工具
- Spotfire 将字符串日期 转换为 日期格式
- python猜数游戏续_python实现猜数游戏
- 计算机内存有何组成,电脑4个4g内存条组成16g内存和16g内存有什么不同?
- 使用LaTeX写数学公式
- python 斗地主发牌_tkinter模拟斗地主发牌
- C语言一些有趣的现象(例子) (译)
- 2.linux中tty、ttySn、ttyUSBn的区别及查看
- Java基础练习——吃货联盟
- 通讯方式:近场通讯和无线通讯
- 前端开发第三方分享/登录功能备忘(facebook、weibo、QQ、weixin微信好友或者朋友圈)
- JavaScript空判断
热门文章
- 跟Gregg学习系统性能--[7.4.5]内存性能指标监测内容
- Validform验证插件
- 搞计算机的离开高校的几个理由
- Unity Lighting参数和烘焙
- Android 启动页页面慢 解决方案
- 《被讨厌的勇气》节选:直面人生,课题分离
- matlab中sort(),atan2(y,x),normrnd(),boxplot()函数,cell数组用法学习
- android studio环境配置
- 早教:平板电脑的新妙用
- 张鑫溢:8.30今日黄金原油是涨还是跌,鲍威尔暗示打压金价走势?