zoukankan      html  css  js  c++  java
  • 漫话C++之string字符串类的使用(有汇编分析)

    C++中并不提倡继续使用C风格的字符串,而是为字符串定义了专门的类,名为string。

    使用前的准备工作

    在使用string类型时,需要包含string头文件,且string位于std命名空间内:

    #include <iostream>
    //...
    std::string str1 = "Hello world!";
    • 1
    • 2
    • 3

    另外需要注意的是,由于历史原因,C++中的字符串字面值并不是C++中std::string类型的对象,而是沿用了C风格字符串。

    string类的构造

    string类一共有三类构造方式,我将其简称为“1,2,3”,即1种空串构造方法,2种直接构造方法,3种拷贝构造方法。 
    + 构造一个空串

    string str0;
    • 1
    • 直接构造
    string str1("Hello world!");
    • 1

    在此情况下,调用的是string类的构造函数,传入的是const char*类型,即string::string(const char *),当然,这里给出的函数进行了简化,实际情况下还涉及分配器等细节,自然要复杂一些。

    • 直接构造重复字符的串
    string str666(6, '6');
    // str666 == "666666"
    • 1
    • 2

    这操作我也是从书上看来的,以前根本没用过,估计以后也很少用上。

    • 拷贝构造 
      • 用C风格字符串拷贝构造(实现方式值得商榷)
    string str2 = "Hello world!";
    • 1

    按理说,在这种情况下应该先调用string::string(const char *)构造函数,为右侧字符串创建一个临时的string对象,再调用str2的拷贝构造函数,传入临时对象的引用。但在VS2017中应该是进行了优化,并没有创建临时对象,而是也形成了直接初始化。 
    两种情况下对应的反汇编代码如下(VS2017):

       79:  string str1("Hello world!");
    00269F34 68 94 DC 26 00       push        offset string "Hello world!" (026DC94h)  
    00269F39 8D 4D CC             lea         ecx,[str1]  
    00269F3C E8 04 72 FF FF       call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (0261145h)  
    00269F41 C7 45 FC 00 00 00 00 mov         dword ptr [ebp-4],0  
        80:     string str2 = "Hello world";
    00269F48 68 D8 DD 26 00       push        offset string "Hello world" (026DDD8h)  
    00269F4D 8D 4D A8             lea         ecx,[str2]  
    00269F50 E8 F0 71 FF FF       call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (0261145h)  
    00269F55 C6 45 FC 01          mov         byte ptr [ebp-4],1  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 传入其他string对象拷贝构造(方法一)
    string str3(str1);
    • 1
    • 传入其他string对象拷贝构造(方法二)
    string str4 = str2;
    • 1

    上述两种拷贝构造方式在VS2017中都生成了相同的汇编代码(反正都是调用拷贝构造函数,代码当然相同喽)

        81:     string str3(str1);
    008B9F59 8D 45 CC             lea         eax,[str1]  
    008B9F5C 50                   push        eax  
    008B9F5D 8D 4D 84             lea         ecx,[str3]  
    008B9F60 E8 D4 73 FF FF       call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (08B1339h)  
    008B9F65 C6 45 FC 02          mov         byte ptr [ebp-4],2  
        82:     string str4 = str2;
    008B9F69 8D 45 A8             lea         eax,[str2]  
    008B9F6C 50                   push        eax  
    008B9F6D 8D 8D 60 FF FF FF    lea         ecx,[str4]  
    008B9F73 E8 C1 73 FF FF       call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (08B1339h)  
    008B9F78 C6 45 FC 03          mov         byte ptr [ebp-4],3  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    常用操作

    输入输出

    • 输出到ostream:<<
    • 接收输入:>>
    • 从输入中取一行:getline(istream, str)

    判断

    • str1.empty():判断str1是否为空串,是则返回true
    • str1 + str2:字符串连接,将str2的内容追加到str1后
    • str1 == str2:判等,若str1与str2内容完全相同,则返回true
    • str1 != str2:是上述操作的逆操作
    • <,<=,>,>=:按字典顺序对字符串进行大小写敏感的比较

    获取或设置

    • str1.size():返回str1的长度(即字符的个数) 
      注意:为了实现平台无关性,该函数返回类型为string::size_type,这是一个无符号的数,可让支持C++11标准的编译器进行自动类型推导:
    auto str1Len = str1.size();
    • 1

    同时需要注意的是,由于该函数返回的是无符号数,在比较时,不应将其与带符号数进行比较,否则对于负数由于存储方式与较大的正数相同,会造成bug。 
    + str1[x]:去除str1中下标为n的字符引用(可get到值也可以set值)

    对字符的处理

    字符处理头文件和常用函数

    C++中将C语言标准库文件ctype.h命名为了cctype,其中包含了一些字符类型的判定和修改函数:

    遍历string中的字符

    方法一——传统for循环

    这种方法大家都应该或多或少的使用过。下面是一段使用此方法将字符串中字母都转为大写的代码:

    string str1("Hello world!");
    for (string::size_type i = 0; i < str1.size(); i++)
    {
        str1[i] = toupper(str1[i]);
    }
    • 1
    • 2
    • 3
    • 4
    • 5

    再次提醒,循环变量的类型应该为string::size_type。

    方法二——decltype简化类型定义的传统for循环

    在上例中,如果觉得手写string::size_type麻烦,而auto又没起到预期效果时,decltype关键字就登场了,其说明i的类型与str1.size()的返回值相同。

    string str1("4ever and 4ever");
    for (decltype(str1.size()) i = 0; i < str1.size(); i++)
    {
        str1[i] = toupper(str1[i]);
    }
    • 1
    • 2
    • 3
    • 4
    • 5

    方法三——基于范围的for循环(range-based for loop)

    无需修改字符串

    这种for循环是C++11给我们的语法糖,和Java中的增强for循环一个套路,不用白不用。

    string str1("4ever and 4ever");
    unsigned int digitNum = 0;
    for (auto c : str1)
    {
        if (isdigit(c))
        {
            digitNum++;
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    方法四——使用迭代器

    其实在方法三中基于范围for循环的原理,就是其在底层利用迭代器完成了对字符串序列的遍历。下面,就让我们自己调用迭代器完成同样的工作。

    无需修改字符串

    对于不需要修改字符串的情况,选择string类的常量迭代器即可。

    string str1("4ever and 4ever");
    unsigned int digitNum = 0;
    for (string::const_iterator itr = str1.begin(); itr != str1.end(); itr++)
    {
        if (isdigit(*itr))
        {
            digitNum++;
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    需要修改字符串

    在需要修改字符串内容的情况下,需要使用string类的非常量迭代器:

    string str1("4ever and 4ever");
    for (string::iterator itr = str1.begin(); itr != str1.end(); itr++)
    {
        *itr = toupper(*itr);
    }
    • 1
    • 2
    • 3
    • 4
    • 5

    需要修改字符串

    将循环变量设置为引用类型即可。还是将字符串中英文字母转为大写的例子,在使用基于范围的for循环后,被简化成了:

    string str1("4ever and 4ever");
    for (auto& c : str1)
    {
        c = toupper(c);
    }
    • 1
    • 2
    • 3
    • 4
    • 5

    最后友情提醒,C++标准中并没有要求标准库检查字符串下标是否合法。一旦下标超出字符串长度范围,并不会向Java一样抛出超出范围,而是将产生越界读写。所以,在进行下标操作时,一定要小心小心再小心!

    版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LPWSTR/article/details/77927659
  • 相关阅读:
    你写的单例真的安全吗?
    CountDownLatch&&CyclicBarrier
    初步认识AQS
    Atomic底层原理
    volatile关键字
    Linux常用服务类相关命令
    线程池
    由浅入深TheradLocal
    synchronized关键字
    .net 中dapper实现事务的三种方式总结
  • 原文地址:https://www.cnblogs.com/findumars/p/9261300.html
Copyright © 2011-2022 走看看