把《c++ primer》读薄(3-1 标准库string类型初探)

时间:2024-01-15 20:53:38

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

问题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 ;
}

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

    string s = "adadad";//字符串字面值复制初始化
string s1 = s;//已有的string对象 复制初始化
string s2(s);//已有的string对象直接初始化
string s3("darfxfw");//字符串字面值直接初始化 //string s(n, '单个字符');此种形式的初始化,只能写为直接初始化的形式!
string s4(, '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;
}

把《c++ primer》读薄(3-1 标准库string类型初探)

注意:因为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;//
cout << nullStr.length() << endl;//
cout << st.length() << endl;//
cout << st.size() << endl;//

注意: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 = "";
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 ( == 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 = "";
string str2 = "";
//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 = "1";
string str2 = "";
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 = "";
string str2 = "";
//str1 < str2
if (str1 < str2)
{
cout << "haha" << endl;//执行
//因为短的str2整体和str1前面部分不匹配,那么比较第一个不匹配的0和1,显然0小
}

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

    string str1 = "";
string str2 = "";
//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("");
string str2("");
string str3 = ""; 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 = ; i != str.size(); i++)
{
cout << str[i] << endl;
}

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

    string str("This is a dog!");
for (string::size_type i = ; 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[]))
{
cout << "haha" << endl;//执行
}

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

测试数字,是就返回ture

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

测试字母,是就返回true

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

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

islower('a');

测试标点符号

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

测试空白字符

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

测试大写字母

    isupper(str[]);//是就返回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字符串的标点符号个数

 #include <iostream>
#include <string>
#include <cctype>
using std::string;
using std::endl;
using std::cin;
using std::cout; int main(void)
{
string str("hello world!!!!");
string::size_type count = ; for (string::size_type i = ; i != str.size(); i++)
{
/*
中文字符没有对应的ASCII码,中文字符占两个字节,如果判断中文标点,会报错!
*/
if (ispunct(str[i]))
{
count++;
}
} cout << count << endl;//打印4 system("pause");
return ;
}

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

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

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

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

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

 #include <iostream>
#include <string>
#include <cctype>
using std::string;
using std::endl;
using std::cin;
using std::cout; int main(void)
{
string str;
string str_receive;
//判断输入的字符串是不是带标点,默认不带
bool isPunction = false;
cout << "现在输入字符串" << endl;
//因为cin忽略开头和以后的空白,不能使用cin,用getline函数获取一行完整的文本
getline(cin, str); for (string::size_type i = ; i != str.size(); i++)
{
if (ispunct(str[i]))
{
//字符串含标点,标志变量设为真
isPunction = true;
}
else
{
str_receive += str[i];
}
}
//假如没有标点存在
if (!isPunction)
{
cout << "输入的字符串没有标点!重新输入!" << endl;
}
else
{
cout << "输入的字符串去掉标点之后=" << str_receive << endl;
} system("pause");
return ;
}

把《c++ primer》读薄(3-1 标准库string类型初探)

如果没有标点

把《c++ primer》读薄(3-1 标准库string类型初探)

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

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

 #include <iostream>
#include <string>
#include <cctype>
using std::string;
using std::endl;
using std::cin;
using std::cout; int main(void)
{
string str;
string str_receive;
cout << "请输入若干字符串,不要太多了!" << endl; while (cin >> str)
{
//连接
//str_receive = str + " ";//这样写不好,最后遗留一个空格
str_receive = str + " " + str_receive;
}
cout << "字符串进行连接:";
cout << "结果为:" << endl << str_receive << endl; system("pause");
return ;
}

把《c++ primer》读薄(3-1 标准库string类型初探)

欢迎关注

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

把《c++ primer》读薄(3-1 标准库string类型初探)