把《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 + "/" + "\n";
    cout << str7;//已经包含了换行符,

这样没问题,因为前面+之后是string对象类型,后面+\n也是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 }
复制代码

 

辛苦的劳动,转载请注明出处,谢谢……
http://www.cnblogs.com/kubixuesheng/p/4119776.html
相关文章
|
5天前
|
存储 C++ 容器
C++STL(标准模板库)处理学习应用案例
使用C++ STL,通过`std:vector`存储整数数组 `{5, 3, 1, 4, 2}`,然后利用`std::sort`进行排序,输出排序后序列:`std:vector&lt;int&gt; numbers; numbers = {5, 3, 1, 4, 2}; std:sort(numbers.begin(), numbers.end()); for (int number : numbers) { std::cout &lt;&lt; number &lt;&lt; &quot; &quot;; }`
13 2
|
15天前
|
存储 C++ 容器
C++入门指南:string类文档详细解析(非常经典,建议收藏)
C++入门指南:string类文档详细解析(非常经典,建议收藏)
28 0
|
11天前
|
JavaScript 前端开发
JavaScript 中如何检测一个变量是一个 String 类型?
JavaScript 中如何检测一个变量是一个 String 类型?
21 2
|
15天前
|
存储 编译器 C语言
C++_String增删查改模拟实现
C++_String增删查改模拟实现
45 0
|
17天前
|
缓存 算法 C语言
【C++ 标准查找算法 】C++标准库查找算法深入解析(In-depth Analysis of C++ Standard Library Search Algorithms)
【C++ 标准查找算法 】C++标准库查找算法深入解析(In-depth Analysis of C++ Standard Library Search Algorithms)
44 0
|
5天前
|
存储 算法 C语言
【C++初阶】8. STL初阶 + String类
【C++初阶】8. STL初阶 + String类
44 1
|
5天前
|
C语言 C++
【C++初阶】9. string类的模拟实现
【C++初阶】9. string类的模拟实现
33 1
|
11天前
|
XML JSON JavaScript
推荐一个比较好用的c++版本http协议库-cpp-httplib
推荐一个比较好用的c++版本http协议库-cpp-httplib
29 1
|
17天前
|
安全 网络性能优化 Android开发
深入解析:选择最佳C++ MQTT库的综合指南
深入解析:选择最佳C++ MQTT库的综合指南
80 0
|
17天前
|
XML 运维 监控
【深入探究 C++ 日志库清理策略】glog、log4cplus 和 spdlog 的日志文件管理策略
【深入探究 C++ 日志库清理策略】glog、log4cplus 和 spdlog 的日志文件管理策略
56 0