督促读书,总结精华,提炼笔记,抛砖引玉,有不合适的地方,欢迎留言指正。
问题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
问题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电子书,资料,帮忙内推,欢迎拍砖!