• 【C++】string类接口的了解和使用


    为什么我们要学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

    总结 


    一、STL的介绍

    STL(standard template libaray- 标准模板库 ) C++ 标准库的重要组成部分 ,不仅是一个可复用的组件库,而且 是一个包罗数据结构与算法的软件框架
    STL 的六大组件 :仿函数、算法、迭代器、空间配置器、容器、配接器。
    这些在我们接下来的学习都会深入学习!
    网上有句话说: 不懂 STL ,不要说你会 C++” STL C++ 中的优秀作品,有了它的陪伴,许多底层的数据结构
    以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。

    二、标准库中的string类

    1、简介string

     string是一个模板,是因为编码不同,导致char的字节数不同,所以需要模板来适应不同的编码类型,原型差不多就是这样的:

     我们接下来研究的:utf-8,char为一个字节的string类

     

     2、库中的string类的常用接口说明

       1.构造函数

    在学习任何类之前,当然要先看它的构造函数了!(我们只了解重要且常用的)

     构造函数:直接上例子:

      可以看得出,s1为默认的构造函数

    s2是带参的构造函数(理解:会开辟一段空间,将内容存起来)

    s3的构造方式,会发生隐式类型转换,会产生临时变量,先构造,再拷贝构造,优化后就是构造

    s4构造是用n个char c 字符去初始化

    拷贝构造:自定义类型拷贝会发生深拷贝,所以形参一般要用引用减少拷贝,提高效率

    s5,s6都是拷贝构造


    2.npos

    npos是一个静态成员变量,无符号整型,是一个极大的数。

    对于这个构造函数,是在str的pos位置开始向后len的长度,这段字符串进行初始化。

    那默认不传len,len的值就是npos,是一个非常大的数,当len大于str的长度时,默认到了str的最后一位。

    3.遍历 

    共有三种遍历方式:

    1. void test2()
    2. {
    3. string s1("12345678");
    4. // 1、下标 []
    5. for (size_t i = 0; i < s1.size(); ++i)
    6. {
    7. s1[i]++;
    8. }
    9. cout << s1 << endl;
    10. // 2、范围for
    11. for (auto& ch : s1)
    12. {
    13. ch--;
    14. }
    15. cout << s1 << endl;
    16. // 3、迭代器 -- 通用的访问形式
    17. string::iterator it1 = s1.begin();
    18. while (it1 != s1.end())
    19. {
    20. *it1 += 1;
    21. ++it1; // 把string里的内容每一个都加了一
    22. }
    23. it1 = s1.begin();
    24. while (it1 != s1.end())
    25. {
    26. cout << *it1 << " "; //打印每一个内容
    27. ++it1;
    28. }
    29. cout << endl;
    30. }

    可以看的出:operator[]有两个接口,一个是有const,一个没有const

    operator[],是一个可读且可写的接口。

    当const只读对象调用时,就会调用const接口

    当只写对象调用时,就会调用非const,

    所以对于即可写又可读的接口函数来说,就有两个版本,const和非const

      

    还有一点:operator[]内部有防止越界访问的功能:assert(pos<=size);

    迭代器的遍历方法:

    这里的迭代器是string类的自定义的一种类型,需要string::

    迭代器我们现在可以看作是 和指针相差不多的东西(行为像指针),但他又不是指针,具体的底层我们后面会见面。

    begin()就是指向第一个位置,end()指向最后一个有效字符的下一个位置

    迭代器要注意的地方:

    我们可以看到:迭代器也分为const和非const,那什么时候分别用哪个呢?

    const_iterator:只能在const对象下使用,并且const迭代器可以改变迭代器本身,但不能改变迭代器所指向的内容 

    迭代器有正向迭代器和反向迭代器:

    1. void Print(const string& s)
    2. {
    3. // 只读不写,可以遍历改变it,但不能改变他指向内容
    4. string::const_iterator it = s.begin(); //正向迭代器
    5. while (it != s.end())
    6. {
    7. // *it += 1; 错误!
    8. cout << *it << " ";
    9. ++it;
    10. //正向迭代器就是正着迭代++
    11. }
    12. }
    13. cout << endl;
    14. //string::const_reverse_iterator rit = s.rbegin();
    15. auto rit = s.rbegin(); //反向迭代器
    16. while (rit != s.rend())
    17. {
    18. cout << *rit << " ";
    19. ++rit;
    20. //都是++,这里不可以类比指针反向迭代器就是反着迭代++
    21. }
    22. cout << endl;
    23. }

    学会了吗?


     3.capacity

    1.size,lenth,max_size,capacity

    在string中,我们会怎么描述字符串长度??length是不是更贴合,那为什么又有size呢??

    因为string是早于stl的,在它之后size更普遍适用,为了普遍化,那只能添加一个size了

    size==length,就是求长度或者字符个数。(length淘汰!!)

    只读接口,加const

     capacity:string的容量,和size可不相同。

    clear:因为stl只是规范了每个接口名字或者参数,但并没有将每一个容器函数的细节拿捏死,所以对于clear,我们并不知道他清空数据以后,是否还要回收空间。得需要验证!

    1. void test4()
    2. {
    3. string s("hello world");
    4. cout << s.size() << endl;
    5. cout << s.length() << endl;
    6. cout << s.capacity() << endl;
    7. s.clear();
    8. cout << s << endl;
    9. cout << s.capacity() << endl;
    10. }

    可以发现,clear之后并没有回收空间,也不会缩容

    empty(),就是来判空的


    2.shrink_to_fit

    当capacity大于size时,将size和capacity保持一致,会缩容,但缩容代价还是挺大的,一般也不这么搞。


    3.reserve、resize(重点)

      只改变capacity

    在我们知道需要多少空间时,插入数据,为了防止反复扩容(扩容代价大),我们可以提前预留空间,开辟好。

    那到底是怎么扩的呢??一次扩多少?

    我们在vs和g++上分别测试得出:vs上面会1.5倍扩容,但是存在内存对齐问题,会有些许偏差,但空间还是大于我们要开辟的。g++就是2倍扩容,要多少扩多少,不会有偏差。

     第一次预留500,第一次扩容到766,第二次扩容1149,差不多就是1.5倍

    resize:它改变的是 size

    当n不同,resize会分为三种情况:(n为size改变后的值)

    举例说明:

    1. void test6()
    2. {
    3. //当n
    4. string s1("hello world");
    5. s1.resize(4);
    6. cout << s1.size() << endl;
    7. cout << s1.capacity() << endl;
    8. cout << s1 << endl << endl;
    9. //当size
    10. string s2("hello world");
    11. s2.resize(15, '#');
    12. cout << s2.size() << endl;
    13. cout << s2.capacity() << endl;
    14. cout << s2 << endl << endl;
    15. //当n>capacity, (扩容+插入数据)
    16. string s3("hello world");
    17. s3.resize(20, '#');
    18. cout << s3.size() << endl;
    19. cout << s3.capacity() << endl;
    20. cout << s3 << endl << endl;
    21. }

    所以resize功能还是比较完善健全的。

     4.operator[],at

     他们是一样的,都是读写到string中的某个值,进行操作。

    但是区别在于:当发生越界时,operator[]会直接assert()报警告;而at则会抛异常(后面我们会详细了解)

    5、Modifiers

    1.push_back, append, operator+=

    插入:

    push_back :尾插

    下面通过举例来观察: 

    1. void test7()
    2. {
    3. //push_back
    4. string s1("hello world");
    5. s1.push_back('x');
    6. s1.push_back('y');
    7. cout << s1 << endl;
    8. //append
    9. string s2("1111111111");
    10. s2.append(s1);
    11. cout << s2 << endl;
    12. //operator+=
    13. string s3("666");
    14. s3 += ' ';
    15. s3 += "hello world";
    16. s3 += s2;
    17. cout << s3 << endl;
    18. }

     

    2.insert,erase

    适用于头插,头删,中间插入和删除

    但这两种函数我们都不建议经常使用,因为它的效率很低,在删除或者插入时,就会有数据挪动,效率很低。

    1. void test8()
    2. {
    3. string s("hello world");
    4. s.insert(0, "bit");
    5. cout << s << endl;
    6. s.erase(0, 3);
    7. cout << s << endl;
    8. s.insert(5, "bit");
    9. cout << s << endl;
    10. s.erase(5,3);
    11. cout << s << endl;
    12. s.erase(5); //第二个参数不提供,默认是npos,直接从pos删到最后一个。
    13. cout << s << endl;
    14. }

     3.assign,replace(不重要)

    assign(功能):先clear,再赋值 

    replace:替换内容

    1. void test9()
    2. {
    3. string s("3456789");
    4. s.assign("1234");
    5. cout << s << endl;
    6. string s1("hello world");
    7. s1.replace(6, 10, "666");
    8. cout << s1;
    9. }

    4.find,c_str,substr

    find:也有缺省值,默认不提供,直接找到最后一个

    c_str:取出C形式字符串(底层指针)

      

    substr:取出str的一部分,再将其构建成新对象返回

      

    getline:输入可以中间带空格的字符串


    总结

    这就是我们经常要用到的函数接口,更底层的内容,需要我们在模拟实现的时候,去好好感悟,下期再见!

  • 相关阅读:
    使用了百度OCR,记录一下
    数据结构 - 二叉树
    MySQL数据库Day04-数据库MySQL的高级使用
    Linux开发工具之调试器gdb
    java性能优化实战:谈一谈服务性能衡量指标有哪些?
    【圆桌论坛】个人作为嘉宾参与问答环节的总结,Create 2024百度AI开发者大会之AI智能体开发与应用论坛
    外包干了五年,废了...
    spring之基于p命名c命名空间的注入
    新手学习:ArcGIS 提取SHP 路网数据、节点
    【nlp】2.6 注意力机制Attention
  • 原文地址:https://blog.csdn.net/ChaoFreeandeasy_/article/details/128009643