国庆没回家,未来7天应该每天一篇笔记。
本来打算把复合类型看完,不过太长了,分两天。
2011-10-01(Compound Types I)
1、字符串的拼接。任何由空白分隔符(空格、制表和换行符)分割的字符串常量都自动拼接成一个:
cout << "One Statement""Another Statement\n";
2、cin以空白符界定读取范围。如:
char front[20];
char tail[20];
cout << "Enter front: ";
cin >> front;
cout << "Enter tail: ";
cin >> tail;
cout << "Result: front = " << front << "; tail = " << tail << endl;
测试:
Enter front: Marine King
Enter tail: Result: front = Marine; tail = King
3、get()和getline()。由于有了上面的问题,所以iostream类提供了两个面向行的字符串输入函数。当然,面向行亦有换行符的处理问题:getline()将'\n'视作读取结束标志,并将其抛弃;get()将'\n'视作读取结束标志,并留在输入队列中。
I、getline()。 getline()是iostream的实例函数,cin是iostream的对象,所以要通过“.”来调用。它的参数有两个,一个是字符数组,一个是你要接受的字符数,即可以用以下方式调用:
cin.getline(char arrayName[], int max);
II、get()。get()有几个形式(即已被重载),其中一种形式与getline()一样,即:
cin.get(char arrayName[], int max);
由于get()是将'\n'留在输入队列中的,所以如果使用get()的方法和getline()一样,将出现问题,即(与2一样的设定):
cin.get(front,20);
cin.get(tail,20);
运行,将会出现如下结果:
Enter front: Marine
Enter tail: Result: front = Marine; tail =
原因是,输入Marine按下的'Enter'键是作为'\n'留在输入队列,下一次调用get(),一开始就读取到读取结束标志'\n',认为已达行尾,程序直接执行下一行。解决的办法是把'\n'从输入队列中去掉,可调用get()的另一种形式,这种形式的功能是字符读取,将'\n'读取:
cin.get(front,20);
cin.get();
cin.get(tail,20);
当然,cin.get(front,20)返回的是一个cin对象,所以也可以采用如下形式:
cin.get(front,20).get();
cin.get(tail,20);
顺便说一句,getline()返回的也是cin的一个对象,所以……都懂的。
4、使用get()和getline()的程序健壮性问题。虽然使用get()在正常情况下代码量多于使用getline(),但还是要建议多使用get(),因为在处理输入字符串长度过长问题上要优于getline()。
#include <iostream>
int main()
{
using namespace std;
char front[10];
char tail[10];
//char ch;
cout << "Enter front: ";
cin.getline(front,10);//.get(ch);
/*if(ch == '\n')
cout << "Chars less than 10" << "; The lost char: " << ch <<endl;
else
cout << "Chars more than 10" << "; The lost char: " << ch <<endl;*/
cout << "Enter tail: ";
cin.getline(tail,10);
cout << "Result: front = " << front << "; tail = " << tail << endl;
return 0;
}
如果第一次输入的是:1234567890123(长度大于等于10),则出现以下结果:
Enter front: 1234567890123
Enter tail: Result: front = 123456789; tail =
即当字符串长度大于等于所设定的最大长度值,截去数组可储存的部分后,余下的并不会被下一个getline()所读取。
将上述程序getline()函数改为get(),并去掉注释部分,运行三次分别输入:123456,654321(length<10);1234567890(length=10);1234567890123(length>10):
第一次结果:
Enter front: 123456
You enter chars less than 10; The lost char:
Enter tail: 654321
Result: front = 123456; tail = 654321
第二次结果:
Enter front: 1234567890
Chars more than 10; The lost char: 0
Enter tail: Result: front = 123456789; tail =
第三次结果:
Enter front: 1234567890123
Chars more than 10; The lost char: 0
Enter tail: Result: front = 123456789; tail = 123
可以看出,get()的优势在于,将包括换行符在内的所有字符都留在输入队列,通过对get()参数的控制和'\n'的取舍任意地读取所需的字符串。
5、string类。C++提供了另一个C中没有的字符串处理方式:使用string类。string是位于名称空间std中的,所以需使用using指令,另外,如果要使用string类,头文件应包括#include <string>。
对string对象的处理方法是取之于C语言而高于C语言:
string str1; //定义长度为0的string对象
string str2 = "abc"; //定义并初始化
str1 = str2; //赋值
string str3 = str1 + str2; //拼接
cout << str2[1] << endl; //string对象具有字符数组性质,输出'b'
cout << str3 << endl; //输出abcabc
通过“+”和“=”而不是C语言中的strcat()和strcpy(),不但书写简便,而且不会有字符数超出数组最大长度的情况。
6、string对象的输入。输出使用cout这个不必提。输入因为cin是以空白符为读取结束标志,使用它有诸多不便,所以要使用:
string str;
getline(cin,str);
来进行整行的字符输入,cin作为一个参数,标明要到哪里去找输入。至于为什么getline()有时需要cin调用(表明是istream的类方法),有时不需要(表明不是类方法),涉及到友元函数,教材没讲清楚,这里不便论述。
7、结构体。
① VC++7.1之后才能使用string类作为结构体成员;
②结构体可定义在所有函数之外也可定义main函数内,应用范围有所不同。当定义在main外的时候,且结构体成员包括string,则名称空间也应声明在main之外,否则应使用std::string。
③结构体初始化一般采用数组形式:
struct Student
{
char name[20];
int number;
};
Student stu1 =
{
"Tom",
25
};
注意到结构体成员之间的逗号。当然也可以写成Student stu1 = {"Tom",25};
④结构体可同时完成声明和创建变量工作:
struct Student
{
char name[20];
int number;
}stu1,stu2;
也可以同时完成初始化的工作:
struct Student
{
//the same as above
}stu1 = {"Tom",25};
不过如此做可读性差点。
⑤结构体可无名称,如struct{int x; int y;}position;以后通过position来访问,但这样后面就不能创建这种类型的结构体变量了。
⑥结构体可赋值,在成员变量有字符数组的情况下也可进行。如,在初始化完stu1而stu2没有的情况下,可通过stu2=stu1赋值。
8、共同体。是一种数据格式,能储存不同的数据类型,但只能同时储存一种。就像一个学生,他/她可以用学号(int型)或者姓名(char[]型)代表。共同体的定义和应用可以如下:
#include <iostream>
#include <string>
using namespace std;
struct Student
{
int type;//if type = 0,ID's form is int,otherwise,is string
union ID
{
int number;
char name[20];
}ID_val;
};
int main()
{
Student stu[3] = { {1},{0},{1} };
for(int i = 0;i<3;i++)
{
cout << "Enter student " << i+1 << ": \n";
if(stu[i].type == 1)
{
cout << "Enter a number as his or her ID: ";
(cin >> stu[i].ID_val.number).get();
}
else
{
cout << "Enter a string as his or her ID: ";
cin.get(stu[i].ID_val.name,20).get();
}
}
for(int i = 0;i<3;i++)
if(stu[i].type == 1)cout << "Student " << i+1 <<"'s ID: " << stu[i].ID_val.number <<endl;
else cout << "Student " << i+1 <<"'s ID: " << stu[i].ID_val.name <<endl;
return 0;
}
输入输出如下:
Enter student 1:
Enter a number as his or her ID: 25
Enter student 2:
Enter a string as his or her ID: Tom
Enter student 3:
Enter a number as his or her ID: 30
Student 1's ID: 25
Student 2's ID: Tom
Student 3's ID: 30
注意到17行,可部分初始化;注意到第24行,用get()来接收输入数字后的换行符,以免影响后面的字符串输入。另外,共同体大小是确定的,等于共同体定义的类型中长度最大的,如此可看出,共同体是不允许定义string类的对象的,因为其大小不确定。
9、匿名共同体。对于上面的例子,可以省去共同体名称及其变量:ID和ID_val。则在调用它的时候,可以直接调用它的成员:
stu[i].number //34行部分修改
stu[i].name //35行部分修改
10、枚举。其实是多个const的组合,即定义多个符号常量。
①定义。用enum关键字定义:
enum Day {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
默认情况下,系统将整数赋给枚举量,Monday = 0,Tuesday =2 ...所以,上面的定义等效于:
const int Monday = 0;
const int Tuesday = 1;
//... ...
可以显式地设置枚举量的值:
enum race {Zerg = 0, Terran = 5, Protoss = 10};
也可以部分设置:
enum race {Zerg, Terran = 5,Protoss};
这时,Zerg = 0,Protoss = 6。
②枚举量与整型。枚举量是整型,可被提升为int类型,但int不能被自动转换成枚举类型:
int day1 = Thursday; //合法,Thursday自动转换成3
Day day2 = 3 //非法,3不能自动转换成Thursday,可通过强制转换:Day day2 = (Day)3;
Day day3 = Monday + Friday;//非法,Monday和Friday自动转换成0和4,所得4不能转换成枚举型day3