目录
string – C++ Reference
1 容量相关
1.1 size/length
size和length表示string里存储的有效数据的大小/长度。
可以用来for循环遍历string:
string str("hello string");
for(int i = 0;i < str.size();++i)
{
cout<<str[i];//[]重载
}
string str("hello string");
for(int i = 0;i < str.length();++i)
{
cout<<str[i];//[]重载
}
注:
length用来表示线性数据结构的有效数据长度大小,size既可以用来表示线性数据结构的有效数据大小、也可以用来表示非线性结构的有效数据大小,可以说size是通用的。
1.2 capacity
capacity表示已经分配的空间的大小,通常capacity>=size/length
1.3 resize
顾名思义,改变string对象的size,有两个函数重载
体现为两个方面:
①如果传入的n<原先的长度,仅仅会删除数据,不作数据修改,因此有效重载函数形式为
void resize(size_t n)
vs编译器:
g++:
②如果传入的n>原先的长度,不仅会改变容量,而且可以指定增加的数据,因此有效函数重载形式为:
void resize (size_t n)
void resize (size_t n, char c)
vs编译器:
指定增加的字符为’x’:
不指定增加的字符,可能放入的都是\0:
g++:
不指定增加的字符,可能放入的都是\0:
1.4 reserve
顾名思义,reserve会改变capacity,而不会改变数据。
当传入的n小于当前的capacity,capacity会被缩小,叫做缩容;当传入的n大于当前的capacity,capacity会被扩大,叫做扩容
不同的编译器对于缩容和扩容的处理不同。
vs编译器:
①扩容机制
string s;
size_t old = s.capacity();
cout << old << endl;
//扩容机制
for (size_t i = 0; i < 100; ++i)
{
s += 'x';
if (old != s.capacity())
{
old = s.capacity();
cout << old << endl;
}
}
②无数据时,缩容到最小容量15;有数据时,不会缩容
g++:
①扩容机制
②缩容顶多缩容至size的大小
1.5 empty
函数返回类型为真或者假
2 运算符重载
2.1 operator=
我们原本以为的赋值运算符重载:
第二行还有报错,报错信息是:
实际上这并不是赋值运算符重载,实际的运算符重载应该是这样的:
vs下=号的颜色都变了,说明两次=的作用是不一样的。为什么呢?
这下我们明白了,之所以第二次是赋值运算符重载,是因为s1、s2、s3都已经初始化好了,而且都是调用的默认构造函数string() 。
赋值运算符的运算顺序是自右向左的,s1先赋值给s2,s2再赋值给s3。
2.2 operator[]
operator[]为我们提供了通过下标直接访问字符串内容的便利,有两种函数重载,一种给普通对象,另一种给const对象。
2.3 operator+(非成员函数)
Concatenate strings:连接字符或字符串
用法:
为什么这个重载是非成员函数呢?
我们都知道成员函数的第一个参数是隐藏的this指针,指向实例化出来的对象, 因此如果把+重载写在类里面也就是成员函数,第一个参数就必然是实例化出来的对象,而相加的两个对象不必一定要有this,可以是其他的两个对象,所以STL库就把它设计成非成员函数。
2.4 operator+=
官网里的例子:
operator+=可以很方便地进行字符串尾插的操作,是尾插最常用且实用的操作,尾插前先检查容量,容量不够先扩容再尾插。下面还有push_back、append、insert等的接口函数,也是用来进行字符串增加的,但是没有+=运算符重载用的那么方便。
2.5 operator>> && operator<<(非成员函数)
为了方便输入和直接输出string里的内容,设计出了流插入和流提取的重载。
这两个必须设计成非成员函数,否则就会导致cout和str的顺序颠倒:str<<cout
因为左右操作数和函数的传参类型要一致,成员函数的第一个参数永远是this指针!
3 增删查改的接口
3.1 push_back
尾插会先检查容量,容量不够先扩容,然后在进行尾插操作。尾插结束后再补一个\0,模拟一下:
void push_back(char c)
{
if (_capacity == _size)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = c;
_size++;
_str[_size] = '\0';
}
3.2 append
append提供了六种函数重载,可以看作push_back的plus版本。
string s = "hello";
string _s = " string";
s.append(_s);//hello string
s.append(_s,0,3);//hello st
s.append(" string");//hello string
s.append(" string", 2);//hello s
s.append(5,'x');//helloxxxxx
s.append(_s.begin(),_s.begin()+2);//hello s
讲解一下第二种和第四种:
substring(2):
string& append (const string& str, size_t subpos, size_t sublen)
string s = "hello";
string _s = " string";
s.append(_s,0,3);//hello st
功能:将副串_s的一部分尾插至主串s之后。
注:
subpos必须小于等于sublen,否则就会:
如果sublen大于strlen(_s),编译器会多开空间吗?
vs编译器:
g++:
答案是不会!
buffer(4):
string& append (const char* s, size_t n)
string s = "hello";
s.append(" string", 3);
cout<<s;//hello st
功能:将常量字符串的一部分尾插至s。
注:
如果传入的参数n大于strlen(s),编译器会多开空间吗?
vs编译器:
g++:
答案是会的!
3.3 insert
由于顺序表头插的时间复杂度为O(n),效率较低,所以STL库并没有实现string的头插接口,而是设计了一个insert,可以用来在pos位置插入字符或字符串,特殊的当pos为0时,就相当于头插。
其中前四种的设计和上面append的设计类似,后三种多了迭代器的传参:
void insert(iterator p,size_t n,char c);
iterator insert (iterator p, char c);
template <class InputIterator>
void insert (iterator p, InputIterator first, InputIterator last);
第一种的用法:
第二种用法:
第三种用法:
3.4 erase
顾名思义,删除字符串中的字符。
用法:
第一种:
缺省参数pos=0,len=(size_t)(-1)=2^32-1
第二种:
传入迭代器,删除迭代器指向的单个字符,并返回迭代器。
顺序是先删除h再把删除后的s.begin()赋值给it。
第三种:
范围删除并返回迭代器,范围是[first,last),左闭右开!
3.5 assign
assign的用法其实就是新串代替旧串,举一个例子就行:
3.6 find
find还是挺好用的,至少不用自己写字符串查找算法,找到了就返回找到的最开始的位置,找不到就返回(size_t)(-1)。
4 其他接口
4.1 substr
功能:返回string的子串,从pos位置返回长度为len的子串。
4.2 c_str()
功能:将string以const char*的形式返回。
string的私有成员:
char* _str;
size_t _size;
size_t _capacity;
模拟实现c_str的话:
const char* c_str()
{
return _str;
}
4.3 rfind
功能:从string的尾巴往前找指定的字符或字符串。
字符串最后一个单词的长度_牛客题霸_牛客网
用rfind解题:
#include <iostream>
using namespace std;
int main()
{
string s;
getline(cin,s);
size_t pos = s.rfind(" ");
cout<<s.size()-pos-1;
return 0;
}
文章出处登录后可见!