zoukankan      html  css  js  c++  java
  • 把《c++ primer》读薄(3-1 标准库string类型初探)

    督促读书,总结精华,提炼笔记,抛砖引玉,有不合适的地方,欢迎留言指正。

    问题1:养成一个好习惯,在头文件中只定义确实需要的东西

    using namespace std;  //建议需要什么再using声明什么,最好不使用这个偷懒的写法

    问题2:C++定义了一个内容丰富的抽象数据类型的标准库,最重要的两个标准库类型是string和vector

    因为他们是c++基本内置类型基础上改进而来,故重要!前者支持变长字符串,后者可以保存一组指定类型的对象。

    问题3:什么时候会调用默认的构造函数?

    默认构造函数,是不带参数的,可以为所有形参提供默认实参。且它是系统默认提供的,在定义类对象的时候,没有提供初始化时会自动调用。

    问题4:初始化string对象的三个方式:

    默认构造函数初始化

    string s;//全局变量,默认初始化为空
    
    int main(void)
    {    
        string s1;//调用string类的默认构造函数,初始化为空
        cout << s << "|" << s1 << endl;//打印|return 0;
    }

    string对象初始化和字符串字面值常量初始化

        string s = "adadad";//字符串字面值复制初始化
        string s1 = s;//已有的string对象 复制初始化
        string s2(s);//已有的string对象直接初始化
        string s3("darfxfw");//字符串字面值直接初始化
    
        //string s(n, '单个字符');此种形式的初始化,只能写为直接初始化的形式!
        string s4(10, 'd');//比较重要的用法!可以初始化s4为10个d组成的字符串
        cout << s4 << endl;

    问题5:cin读取string的特点

        string s;
        //从标准输入读取string类型数据,存储在s
        //写法:cin >> s;        注意:
        //1、读取的时候忽略开头的所有空白字符(tab,space,enter等)
        //2、再次遇到空白字符,自动结束输入,类似c语言里的scanf函数,遇到空白就停止输入!
        //cout << s << endl;
        //测试输入:     123   456 ,输出123,前面的空白,和后面的空白被自动忽略!
        //测试输入:           ,输出空,同理
        
        //同时输入多个string对象
        //1、可以空格为区分,如
        string ss;
        cin >> s >> ss;//测试:先输入hello,然后输入空格(可多个),继续输入world,则最后输出helloworld,空格不被读取
        //2、既然可以用 不限个数的 空字符区分,自然可以输入回车换行之后继续输入ss,效果一样
        cout << s << ss << endl;

    注意:程序开头要包含头文件和using声明

    #include <iostream>
    #include <string>
    using std::string;
    using std::endl;
    using std::cin;
    using std::cout;

    问题6:如果想读取空白字符,那么不能用输入流,而是需要使用getline()函数

        string line;
        //不忽略空白字符,但是换行例外!也就是说,只要getline函数遇到换行,就自动结束读取,其他空白不会!结束输入后返回istream对象
        //如果是在开始输入的时候,就输入换行,那么输入就会终止,返回一个空串!
        while (getline(cin, line))
        {
            //实现输入一行文本,打印一行文本
            cout << line << endl;
        }

    注意:因为getline函数以换行为结束,且是忽略换行!那么依然要使用endl换行,之所以遇到换行就结束输入,是因为getline()是用来读取一整行内容的函数。

    问题7:比较string的cin输入和getline输入方式

    cin适合读取一个单词,getline函数适合读取一行文本,如:

    string in;
    //cin返回所读的istream对象,但是仅仅适合读入单词,如果开头遇到任何空白字符,则被忽略!即使是换行也是忽略掉!后续遇到空白则终止读取
    while (cin >> in)
    {
        cout << in << endl;
    }
    //直到遇到文件末尾且是无效输入,或者ctrl+z符号(不同操作系统不一样),则结束循环

    小结:当遇到需要每次读入一个单词的要求,则使用cin+循环,如果是每次需要读取一行内容,则使用getline()函数+循环。且还有一个问题,如果使用cin读取string对象,那么我们知道开头或者中间的任何空白都被忽略,且后续遇到空白字符,就终止读取,但是此时,空白字符还是留在了输入流内!而getline恰恰相反,不会忽略开头的空白字符,遇到换行符就结束读取!但是会丢弃换行符,不会保留在输入流内!更不会存到string对象里!

    问题8:再论,为何不建议偷懒的直接使用using namespace xxx;俗称using指示,而建议使用using声明?

    就怕习惯了,在大型程序里,经常使用不同的多个库,如用using指示,就会包含全部库的名字空间,有可能出现命名冲突。标准库std虽然不会,但是专家建议别怕麻烦。

    问题9:字符串字面值常量和string类型不是同一个类型!

    目的是兼容c语言而规定,不要混淆两者。注意区分!

    问题10:string对象的求长度操作

    求长度,指的是string对象中字符的个数

    string st("hello world!");
    string nullStr;
    cout << nullStr.size() << endl;//0
    cout << nullStr.length() << endl;//0
    cout << st.length() << endl;//12
    cout << st.size() << endl;//12

    注意:basic_string<>有双重身份,一是代替传统的C字符串,所以应该针对C中的strlen给出相应的函数length()。另一个身份是可以用作STL容器,所以要按照STL容器的惯例给出size(),两者功能一样。

    问题11:为什么不建议把size()函数的返回值赋值给c/c++基本内置类型int or其他整型?

    string类的size()成员函数返回的不是整型,而是string::size_type类型,这样的类型叫库类型的配套类型,目的是实现机器无关性!因为不同机器的int类型或者无符号int的大小不一样!平台换了,则一样的程序可能出现类型的长度溢出错误

    注意:size_type功能上和 无符号的 int或者 无符号的 long int一样大小,但是只是功能一样!千万不要随便的赋值给int类型,他们不是一个类型!针对不同平台,不同存储的string对象,返回的大小极有可能和内置类型的范围不一样!引起溢出错误!

    且显式的使用返回值时,应该加上string::size_type,来说明这个类型是string类定义的

        string str = "1111";
        int len = str.size();//不是不对,而是不建议这样用,没有超过范围,这样是ok的,但是要禁止类似写法!
        cout << len << endl;//4

    看下对应的汇编源码:

    int len = str.size();
    011233E4  lea         ecx,[ebp-34h]  
    011233E7  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::size (11212FDh)  
    011233EC  mov         dword ptr [ebp-40h],eax 

    发现内部实现机制确实是这样调用的:std::basic_string<char,std::char_traits<char>,std::allocator<char> >::size ()

    问题12:string对象判空操作

    string str = "";
    //判空,数据结构中常见啊,empty()函数返回bool值,空就是true,不空就是false
    if (str.empty())
    {
        cout << "NULL" << endl;
    }
    //打印NULL
    //当然也可以使用求长度判断string串空 string str1; if (0 == str1.length()) { cout << "str1空" << endl; } //打印str1空

    问题13:string对象的关系操作(串比较),使用运算符重载,实现比较对象的功能!返回bool类型

    1、string对象比较操作,区分大小写!比如

        string a = "aaa";
        string A = "AAA";
        if (a == A)
        {
            cout << "haha" << endl;//没有执行!说明两个对象不等!
        }

    2、判等或不等,要比较两点,一是长度,二是内容

        string a = "aaa";
        string A = "aaa";
        if (a == A)
        {
            cout << "haha" << endl;//执行
        }

    而下面就不是相等的

        string a = "aaa";
        string A = "aa";
        if (a != A)
        {
            cout << "haha" << endl;//执行
        }

    问题14:string对象的关系比较比的谁大,谁小的依据是什么?

    比较原理是字典排序的原理

    1、如果s1和s2长度不一样,且短的整体和长的前面某连续的部分匹配,那么比较长度,判断结果

        string str1 = "123";
        string str2 = "1234567890";
        //str1 < str2
        if (str1 < str2)
        {
            cout << "haha" << endl;//执行
        }

    再看

        string str1 = "hello";
        string str2 = "hello world";
        //str1 < str2
        if (str1 < str2)
        {
            cout << "haha" << endl;//执行
        }

    2、如果s1和s2长度不一样,且短的整体和长的前面部分没有匹配,那么只需比较第一个不匹配的字符来做判断

        string str1 = "1000213";
        string str2 = "1234567890";
        if (str1 < str2)
        {
            cout << "haha" << endl;//不执行
            //因为字符不匹配,那么比较第一个不匹配的0和2,显然0小
        }

    再看

        string str1 = "hi";
        string str2 = "hello world";
        if (str1 < str2)
        {
            //1和2长度不等,且hi和串2前面没有匹配,那么比较第一个不匹配的字符
            cout << "haha" << endl;//不执行
            //abcdefghijk……e在i前面,说明str2小,就是1>2
        }

    即使str1长度比str2大,此时此景,比较和长度无关

        string str1 = "0000213000000";
        string str2 = "1234567890";
        //str1 < str2
        if (str1 < str2)
        {
            cout << "haha" << endl;//执行
            //因为短的str2整体和str1前面部分不匹配,那么比较第一个不匹配的0和1,显然0小
        }

    3、如果s1和s2长度一样,那就直接看s1和s2的字符匹配否,匹配就是相等,不匹配就比较第一个不匹配的字符,来判断大小

        string str1 = "0000213000";
        string str2 = "1234567890";
        //str1 < str2
        if (str1 < str2)
        {
            cout << "haha" << endl;//执行
            //因为字符不匹配,那么比较第一个不匹配的0和1,显然0小
        }

    对于有大写,有小写,或者数字的情况,是依据ASCII码,大写字母码值比0-9的数字小,数字又比小写字母小(也就是大写字母在最前面,其次是数字,然后小写最后面),也就是任何大写字母的string对象和小写比较,都是小于关系!

        string str1 = "h1";
        string str2 = "hello world";
        if (str1 < str2)
        {
            //ASCII码表编码值,大写 < 数字 < 小写
            cout << "haha" << endl;//执行
        }

    问题15:string对象的赋值操作

    把一个string对象赋值给另一个string对象

        string str1;
        string str2("hdaf");
        str1 = str2;
        cout << str1 << endl;//hdaf
        cout << str2 << endl;//hdaf

    string串对象赋值操作的底层实现机制

    1、先把str1占有的内存释放

    2、分配给str1满足存放str2副本的内存空间

    3、把str2所有字符复制到str1的新内存空间内

    这样的繁琐操作,导致大部分的string库类型的赋值操作效率比较低!

    问题16:string串对象的连接操作,使用运算符重载的+和+=号

    连接string对象

        string str1("123");
        string str2("456");
        string str3 = "789";
    
        string str4 = str1 + str2 + str3;
        cout << str4 << endl;//打印123456789
    
        //string str5 += str4;error C2143: 语法错误 : 缺少“;”(在“+=”的前面),不允许这样初始化string字符串对象,初始化和赋值不一样!
        string str5;
        str5 += str4;
        cout << str5 << endl;//打印123456789

    连接string对象和字符串字面值常量

        string str6 = str1 + "," + str2;
        cout << str6 << endl;//打印123,456

    注意,这样混合连接,必须保证+操作符的两边至少有一个操作数是string对象!

        string str7 = str1 + "/" + "
    ";
        cout << str7;//已经包含了换行符,

    这样没问题,因为前面+之后是string对象类型,后面+ 也是ok的。比如如下就是错误的:

        //string str8 = "123" + "456"; error C2110: “+”: 不能添加两个指针
        string str8 = "hello " + "," + str4;//同样的错误,前面+还是都是字面值常量

    问题17:string串对象下标操作的陷阱

        //类似数组,通过[]访问string字符串的单个字符,下标也叫索引,从0开始,同样超出下标作用范围的引用会出现溢出问题,且下标返回的值可以作为左值、右值
        //注意,下标操作符类型不是int,而是string::size_type类型
        string str("This is a dog!");
        for (int i = 0; i != str.size(); i++)
        {
            cout << str[i] << endl;
        }

    上面的写法,严格来说,是错的!即使运行对,因为前面说过,这样失去了c++设计库类型跨平台的初衷!容易溢出错误!改为:

        string str("This is a dog!");
        for (string::size_type i = 0; i != str.size(); i++)
        {
            cout << str[i];//打印This is a dog!
        }

    因为size函数返回类型不是整型!在计算下标值的时候,最好不要用内置整型类型!且不要越界!范围是0~(length-1),类似c和c++的数组下标范围。因为c++标准库对索引的范围不作检测!这就需要程序员手动注意!

    问题18:string对象对字符的处理操作

    不仅针对string的字符,对其他char类型也适用。这些操作定义在头文件cctype.h中,数量很多:

        string str("123abc。!?");
        char c = ' ';

    测试是否为数字或者字母

        //如果字符是字母或数字,返回true
        if (isalnum(str[0]))
        {
            cout << "haha" << endl;//执行
        }

    很好理解,is一般是判断函数的前缀名称,num=number代表判断数字0-9,al=alphabet代表判断字母表的字母

    测试数字,是就返回ture

        //如果字符是数字,返回true,digital数字的
        if (isdigit(str[0]))
        {
            cout << "haha" << endl;//执行
        }

    测试字母,是就返回true

        if (isalpha(str[3]))
        {
            cout << "haha" << endl;//执行
        }

    测试小写字母,是就返回true

    islower('a');

    测试标点符号

    ispunct(str[7]);//是就返回ture,punctuation标点符号的意思

    测试空白字符

        isspace(' ');//是就返回true

    测试大写字母

        isupper(str[4]);//是就返回true

    测试16进制数

        isxdigit(0x1111);//是就返回ture

    大写、小写转换

        char cc = 'a';
        cout << toupper(cc) << endl;//如果参数cc是小写,则返回大写,否则直接返回cc
        //cout默认输出的是字母的ASCII码值
        cout << tolower(cc) << endl;//如果参数cc是大写,则返回小写,否则直接返回cc

    大写A的ASCII值为65,小写a为97,大写排在小写前面,码值娇小

    测试是否是可打印字符

        isprint(c);//如果是可以打印的字符,返回true

    可以打印的字符,比如:数字0-9,字母a-z,A-Z,空白字符是空格 ,回车,水平和垂直制表,换行,进纸符,标点符号是除了字母,数字或者可打印空白字符以外的其他可打印字符。

    测试是否为空格,如果不是空格但是可以打印,返回ture,否则false

        isgraph(c);

    测试是否为控制字符,control缩写cntrl

        iscntrl(c);//是返回true
    控制字符(Control Character),出现于特定的信息文本中,表示某一控制功能的字符。
    在ASCⅡ码中,第0~31号及第127号(共33个)是控制字符或通讯专用字符,如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)、BEL(振铃)等;通讯专用字符:SOH(文头)、EOT(文尾)、ACK(确认)等。

    问题19:c版本的头文件在c++的写法

    cctype头文件就是c的type.h头文件,即c++的c版本头文件,不要写.h,而是前面加c。且cname头文件都定义在了命名空间std内部,而.h文件没有这样定义!

    建议使用cname形式的头文件,在c++里。即使他们的内容是一样的!这样做的目的是为了和标准库std保持一致!

    问题20:计算给定string字符串的标点符号个数

     1 #include <iostream>
     2 #include <string>
     3 #include <cctype>
     4 using std::string;
     5 using std::endl;
     6 using std::cin;
     7 using std::cout;
     8 
     9 int main(void)
    10 {    
    11     string str("hello world!!!!");
    12     string::size_type count = 0;
    13 
    14     for (string::size_type i = 0; i != str.size(); i++)
    15     {
    16         /*
    17         中文字符没有对应的ASCII码,中文字符占两个字节,如果判断中文标点,会报错!
    18         */
    19         if (ispunct(str[i]))
    20         {
    21             count++;
    22         }
    23     }
    24 
    25     cout << count << endl;//打印4
    26 
    27     system("pause");
    28     return 0;
    29 }

    汉字不是用ascii表示的,汉字有其单独的编码。GB2312,GBK……一个汉字是有两个字节组成,具体参见:GB2312-80,ASCII码表针对的是西洋文字。

    问题21:判断下面程序合法性

        string s;
        cout << s[0] << endl;

    报错,程序发生中断,看似是输出string字符串对象的第一个字符,但是发现,s是一个空字符串,长度=0,故s[0]不管用!在vs2010中编译出错,程序中断。

    问题22:从string字符串中去掉标点,要求如果输入字符串包含标点,输出则是去掉标点的字符串

     1 #include <iostream>
     2 #include <string>
     3 #include <cctype>
     4 using std::string;
     5 using std::endl;
     6 using std::cin;
     7 using std::cout;
     8 
     9 int main(void)
    10 {    
    11     string str;
    12     string str_receive;
    13     //判断输入的字符串是不是带标点,默认不带
    14     bool isPunction = false;
    15     cout << "现在输入字符串" << endl;
    16     //因为cin忽略开头和以后的空白,不能使用cin,用getline函数获取一行完整的文本
    17     getline(cin, str);
    18 
    19     for (string::size_type i = 0; i != str.size(); i++)
    20     {
    21         if (ispunct(str[i]))
    22         {
    23             //字符串含标点,标志变量设为真
    24             isPunction = true;
    25         }
    26         else
    27         {
    28             str_receive += str[i];
    29         }
    30     }
    31     //假如没有标点存在
    32     if (!isPunction)
    33     {
    34         cout << "输入的字符串没有标点!重新输入!" << endl; 
    35     }
    36     else
    37     {
    38         cout << "输入的字符串去掉标点之后=" << str_receive << endl;
    39     }
    40 
    41     system("pause");
    42     return 0;
    43 }

    如果没有标点

    发现,c++在处理字符串问题上,比c要方便。

    问题23:读取多个string对象,把他们连接为新串,然后把组成新串的字符串对象用空格隔开

     1 #include <iostream>
     2 #include <string>
     3 #include <cctype>
     4 using std::string;
     5 using std::endl;
     6 using std::cin;
     7 using std::cout;
     8 
     9 int main(void)
    10 {    
    11     string str;
    12     string str_receive;
    13     cout << "请输入若干字符串,不要太多了!" << endl;
    14 
    15     while (cin >> str)
    16     {
    17         //连接
    18         //str_receive = str + " ";//这样写不好,最后遗留一个空格
    19         str_receive = str + " " + str_receive;
    20     }
    21     cout << "字符串进行连接:";
    22     cout << "结果为:" << endl << str_receive << endl;
    23 
    24     system("pause");
    25     return 0;
    26 }

     

    欢迎关注

    dashuai的博客是终身学习践行者,大厂程序员,且专注于工作经验、学习笔记的分享和日常吐槽,包括但不限于互联网行业,附带分享一些PDF电子书,资料,帮忙内推,欢迎拍砖!

  • 相关阅读:
    Python 3基础教程1-环境安装和运行环境
    Python+Selenium框架设计篇之-什么是POM
    CS/BS架构的特点
    HTTP 方法:GET 对比 POST
    apt的通讯信道是如何发现的?
    安全运维到运营
    僵尸网络是如何控制你的电脑的?
    了解资产,减少网络攻击面的威胁
    ISC安全大会感受
    mysql 在忘记密码不能登录情况下的设置方法
  • 原文地址:https://www.cnblogs.com/kubixuesheng/p/4119776.html
Copyright © 2011-2022 走看看