重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
为什么我们要学string类呢?那是必须是为了方便啊!在C语言中,我们创建一个字符串,有很多操作或者必须要注意的细节会把控不住,所以C++中出现了string类,让我们应对字符串等oj题也方便快捷了许多!
创新互联提供高防服务器、云服务器、香港服务器、达州电信机房等目录
一、STL的介绍
二、标准库中的string类
1、简介string编辑
2、库中的string类的常用接口说明编辑
1.构造函数
2.npos
3.遍历
3.capacity
1.size,lenth,max_size,capacity
2.shrink_to_fit
3.reserve、resize(重点)
4.operator[],at
5、Modifiers
1.push_back, append, operator+=
2.insert,erase
3.assign,replace(不重要)
4.find,c_str,substr
总结
string是一个模板,是因为编码不同,导致char的字节数不同,所以需要模板来适应不同的编码类型,原型差不多就是这样的:
我们接下来研究的:utf-8,char为一个字节的string类
2、库中的string类的常用接口说明1.构造函数在学习任何类之前,当然要先看它的构造函数了!(我们只了解重要且常用的)
构造函数:直接上例子:
可以看得出,s1为默认的构造函数
s2是带参的构造函数(理解:会开辟一段空间,将内容存起来)
s3的构造方式,会发生隐式类型转换,会产生临时变量,先构造,再拷贝构造,优化后就是构造
s4构造是用n个char c 字符去初始化
拷贝构造:自定义类型拷贝会发生深拷贝,所以形参一般要用引用减少拷贝,提高效率
s5,s6都是拷贝构造
npos是一个静态成员变量,无符号整型,是一个极大的数。
对于这个构造函数,是在str的pos位置开始向后len的长度,这段字符串进行初始化。
那默认不传len,len的值就是npos,是一个非常大的数,当len大于str的长度时,默认到了str的最后一位。
3.遍历共有三种遍历方式:
void test2() { string s1("12345678"); // 1、下标 [] for (size_t i = 0; i< s1.size(); ++i) { s1[i]++; } cout<< s1<< endl; // 2、范围for for (auto& ch : s1) { ch--; } cout<< s1<< endl; // 3、迭代器 -- 通用的访问形式 string::iterator it1 = s1.begin(); while (it1 != s1.end()) { *it1 += 1; ++it1; // 把string里的内容每一个都加了一 } it1 = s1.begin(); while (it1 != s1.end()) { cout<< *it1<< " "; //打印每一个内容 ++it1; } cout<< endl; }
可以看的出:operator[]有两个接口,一个是有const,一个没有const
operator[],是一个可读且可写的接口。
当const只读对象调用时,就会调用const接口
当只写对象调用时,就会调用非const,
所以对于即可写又可读的接口函数来说,就有两个版本,const和非const
还有一点:operator[]内部有防止越界访问的功能:assert(pos<=size);
迭代器的遍历方法:
这里的迭代器是string类的自定义的一种类型,需要string::
迭代器我们现在可以看作是 和指针相差不多的东西(行为像指针),但他又不是指针,具体的底层我们后面会见面。
begin()就是指向第一个位置,end()指向最后一个有效字符的下一个位置
迭代器要注意的地方:
我们可以看到:迭代器也分为const和非const,那什么时候分别用哪个呢?
const_iterator:只能在const对象下使用,并且const迭代器可以改变迭代器本身,但不能改变迭代器所指向的内容
迭代器有正向迭代器和反向迭代器:
void Print(const string& s) { // 只读不写,可以遍历改变it,但不能改变他指向内容 string::const_iterator it = s.begin(); //正向迭代器 while (it != s.end()) { // *it += 1; 错误! cout<< *it<< " "; ++it; //正向迭代器就是正着迭代++ } } cout<< endl; //string::const_reverse_iterator rit = s.rbegin(); auto rit = s.rbegin(); //反向迭代器 while (rit != s.rend()) { cout<< *rit<< " "; ++rit; //都是++,这里不可以类比指针反向迭代器就是反着迭代++ } cout<< endl; }
学会了吗?
在string中,我们会怎么描述字符串长度??length是不是更贴合,那为什么又有size呢??
因为string是早于stl的,在它之后size更普遍适用,为了普遍化,那只能添加一个size了
size==length,就是求长度或者字符个数。(length淘汰!!)
只读接口,加const
capacity:string的容量,和size可不相同。
clear:因为stl只是规范了每个接口名字或者参数,但并没有将每一个容器函数的细节拿捏死,所以对于clear,我们并不知道他清空数据以后,是否还要回收空间。得需要验证!
void test4()
{
string s("hello world");
cout<< s.size()<< endl;
cout<< s.length()<< endl;
cout<< s.capacity()<< endl;
s.clear();
cout<< s<< endl;
cout<< s.capacity()<< endl;
}
可以发现,clear之后并没有回收空间,也不会缩容
empty(),就是来判空的
当capacity大于size时,将size和capacity保持一致,会缩容,但缩容代价还是挺大的,一般也不这么搞。
只改变capacity
在我们知道需要多少空间时,插入数据,为了防止反复扩容(扩容代价大),我们可以提前预留空间,开辟好。
那到底是怎么扩的呢??一次扩多少?
我们在vs和g++上分别测试得出:vs上面会1.5倍扩容,但是存在内存对齐问题,会有些许偏差,但空间还是大于我们要开辟的。g++就是2倍扩容,要多少扩多少,不会有偏差。
第一次预留500,第一次扩容到766,第二次扩容1149,差不多就是1.5倍
4.operator[],atresize:它改变的是 size
当n不同,resize会分为三种情况:(n为size改变后的值)
举例说明:
void test6() { //当n
capacity, (扩容+插入数据) string s3("hello world"); s3.resize(20, '#'); cout<< s3.size()<< endl; cout<< s3.capacity()<< endl; cout<< s3<< endl<< endl; } 所以resize功能还是比较完善健全的。
他们是一样的,都是读写到string中的某个值,进行操作。
但是区别在于:当发生越界时,operator[]会直接assert()报警告;而at则会抛异常(后面我们会详细了解)
5、Modifiers 1.push_back, append, operator+=2.insert,erase插入:
push_back :尾插
下面通过举例来观察:
void test7() { //push_back string s1("hello world"); s1.push_back('x'); s1.push_back('y'); cout<< s1<< endl; //append string s2("1111111111"); s2.append(s1); cout<< s2<< endl; //operator+= string s3("666"); s3 += ' '; s3 += "hello world"; s3 += s2; cout<< s3<< endl; }
适用于头插,头删,中间插入和删除
但这两种函数我们都不建议经常使用,因为它的效率很低,在删除或者插入时,就会有数据挪动,效率很低。
3.assign,replace(不重要)void test8() { string s("hello world"); s.insert(0, "bit"); cout<< s<< endl; s.erase(0, 3); cout<< s<< endl; s.insert(5, "bit"); cout<< s<< endl; s.erase(5,3); cout<< s<< endl; s.erase(5); //第二个参数不提供,默认是npos,直接从pos删到最后一个。 cout<< s<< endl; }
4.find,c_str,substrassign(功能):先clear,再赋值
replace:替换内容
void test9() { string s("3456789"); s.assign("1234"); cout<< s<< endl; string s1("hello world"); s1.replace(6, 10, "666"); cout<< s1; }
find:也有缺省值,默认不提供,直接找到最后一个
c_str:取出C形式字符串(底层指针)
substr:取出str的一部分,再将其构建成新对象返回
getline:输入可以中间带空格的字符串
这就是我们经常要用到的函数接口,更底层的内容,需要我们在模拟实现的时候,去好好感悟,下期再见!
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧