C++学习笔记(三)
复合类型
1.复合类型是基于基本整型和浮点类型创建的,复合类型包括:数组(可以存储多个同类型的值)、特殊的数组(可以存储字符串(一系列的字符串))、结构(可以存储多个不同类型的值)、指针(将数据所处位置告诉计算机变量)、类。
数组(array):
- 一种数据格式,能够存储多个同类型的值。每个值都存储在一个独立的数组元素中,计算机在内存中依次存储数组哥各个元素。
- 数组声明:①存储在每个元素中的值的类型;②数组名;③数组中的元素数。
声明数组的通用格式:typeName arrayName[arraySize];注:arraySize必须是整型常量或者const值,也可以是常量表达式(8 * sizeof(int)),也就是说其中的值在编译之前都是已知的(arraySize不能是变量,变量的值实在程序运行时设置的,也可以用new运算符来避开这种限制),例:int studentAge[30]; - 数组可以单独访问数组元素,使用下标或索引来对元素进行编号(从0开始)。例:int studentAge[10];在数组studentAge中
studentAge[0]表示访问数组的第一个元素,
studentAge[1]表示访问数组的第二个元素… - studentAge[9]表示访问数组的最后一个元素。数组studentAge的长度是10(注意:下标或索引必须小于数组的长度。下标的范围[0, 9])。数组声明能够使用一个声明创建大量的变量,然后可以用索引来标识和访问各个元素。
- 数组元素赋值及初始化:
int studentAge[3];//声明数组
studentAge[0] = 20;//数组下标为0的元素赋值
studentAge[1] = 20; //数组下标为1的元素赋值
studentAge[2] = 20; //数组下标为2的元素赋值
char chArr[4] = {‘A’, ‘B’, ‘C’, ‘D’};//初始化数组
可以使用sizeof运算符(返回类型或者数据对象的长度(单位为字节)),用于数组,将会得到数组中的字节数。如果将sizeof用于数组元素,则得到的将是元素的长度(单位为字节)。chArr是一个数组,而chArr[1]只是一个char类型的变量。
- 数组初始化规则:
限制了数组初始化时刻,决定了数组的元素数目与初始化器中值的数目不想同时将发生的情况。
① 只有在定义数组时才能使用初始化,此后不能使用了,同时也不能将数组赋值给另一个数组。(可以使用下标分别给数组中的元素赋值)。
② 初始化数组时,提供的值可以少于数组的元素数目。(如果只对数组的一部分进行初始化,则编译器将把其他元素设置为0(int类型),把数组中所有的元素初始化为0的方法:{0}(int类型的数组),如果把第一个元素设置为1则表示,int数组中第一个元素是1,其他元素是0)。
③ int things[] = {1, 3, 5, 7};如果初始化数组时方括号内为空(即[]),C++将计算元素个数。所以things数组包含4个元素。(不太建议这样初始化数组).
④ 数组初始化时可以省略等号:int earnings[4] {2, 4 , 6, 7};
⑤ 可不在大括号内包含任何东西,这将把所有元素都设置为零:unsigned int counts[10]={};或者float balances[100]{};
⑥ 列表初始化禁止缩窄转换:错误示例一:long plifs[] = {25, 92, 3.0};其中浮点数3.0转换为整型时缩窄操作,即使浮点数后面的小数点后面为零。错误示例二:char slifs[4]{‘h’, ‘I’, 1122011, ‘\0’};1122011超出了char变量的取值范围。 - C++中数组的替代品:模板类vector(向量、容器);C++11新增的模板类array。
字符串:
- 字符串时存储在内存的连续字节中的一系列字符(意味着可以将字符串存储在char数组中,其中每个字符都位于自己的数组元素中。)。C++中处理字符串的方式有两种:
① C-风格字符串(来自C语言):以空字符结尾"\0",
例:char cat[8]={‘f’, ‘a’, ‘t’, ‘e’, ‘s’, ‘s’, ‘a’, ‘\0’};cout << cat << endl;
输出的是:fatessa。
② 基于string类库的方法。 - 另一种将字符数组初始化为字符串的方法—只需使用一个引号括起的字符串即可,这种字符出被称为字符串常量或字符串字面值,例:char bird[11] = “Mr.Cheeps”;或者char fish[] = “Bubbles”;用引号括起的字符串隐式的包括结尾的空字符(‘\0’),因此不用显式的包括它。(在确定存储字符串所需的最短数组时,把结尾的空字符也要计算在内,例:static char boss[3] = “ab”;此时达到数组boss的最大长度,包含隐式字符’\n’。)。
- 必须使用关键字static来初始化数组。例:static char name[10] = “abcdefg”;
- 字符串常量(使用双引号)不能与字符常量(使用单引号)互换。错误示例:char size = “S”;此时”S”不是字符常量,它表示的是两个字符(S和\0)组成的字符串。”S”实际上表示的是字符串所在的内存地址,因此上面的语句试图将一个内存地址赋值给size(由于地址在C++中是一种独立的类型,所以编译器不允许这种不合理的做法 )。
- 字符串常量拼接:
cout << “W” “Z” “H” << endl;任何两个由空白(空格、制表符和换行符)分隔的字符串常量都将自动拼接成一个(拼接时不会再被链接的字符串之间添加空格,也就是手,第二个字符串的第一个字符将紧跟在第一个字符串的最后一个字符(不考虑\0)后面。第一个字符串中的\0字符将会被第二个字符串中的第一个字符取代)。所以上述代码等效于cout << “WZH” << endl。 - 将字符串存储到数组中:
①将数组初始化位字符串常量;②将键盘或文件输入读入到数组中。使用strlen()来确定字符串的长度(注:strlen函数只计算可见的字符。头文件cstring或者string.h)。char name[7] = “abcdef”;name[5] = ‘\0’;cout << name;//输出为:abcde。 - 读取一行字符串:getline()、get()。两个函数都是读取一行输入,知道到达换行符。其中getline()将丢弃换行符,而get()将换行符保留在输入序列中。
- getline():通过回车键的输入的换行符来确定输入结尾。方法调用cin.getline();函数有两个参数,第一个参数是用来存储输入行的数组的名称(或者定义数组的变量名),第二个参数是要读取的字符数(如果参数为20,则函数最多读取19个字符,余下的空间用于存储自动在结尾处添加的空字符’\0’),第三个为可选参数,设置结束字符(只有匹配到结束字符,getline函数调用才会结束,否则会一直输入)。例:char name[20]{};cin.getline(name, 20, ‘\n’);
getline()另一种使用方式:string name;getline(cin, name);
- get(): 该函数有几种变体,其中一种变体的工作方式与getline()类似,接受的参数相同,解释参数的方式也相同,并且都读取到行尾。但是get函数并不再读取并丢弃换行符,而是将其留在输入队列中。例:cin.get(name, 15);cin.get(id, 30);此时id中是没值的。可以使用另一个变体解决,cin.get()函数,不需要参数。
- cin.getline()和cin.get()区别:getline()使用起来简单一些,get()使得检查错误更简单一些。
- C++中修改控制台输出样式:引入头文件”windows.h”,
SetConsoleTextAttribute (GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_RED | FOREGROUND_GREEN);
- 输入被阻断时,可以使用cin.clear()恢复输入。
- 混合输入时,需要用插入cin.get()。例:int age;char name[20];cin >> age;cin.get();cin.get(name, 20);
string类:
- 使用string类需要包含头文件:#include “string.h”。String类位于名称空间(命名空间)std中。所以必须提供using编译指令,或者使用std::string来引用它。(在很多方面,使用string对象的方式与使用字符数组相同)。string对象和字符数组之间最主要的区别是,可以将string对象声明为简单变量,而不是数组。例:string str1;string str2 = “C++”;程序会自动处理string的大小。
- string初始化:string name = {“xiaoming”};string name{“xiaohong”};string name = “xiaogang”;
- string赋值和拼接:string s1 = “0”;string s2 = “1”;string s3 = s1 + s2;(s3=”01”)或者s3 = (s1+=s2);总结:可以使用运算符+将两个string对象合并起来,也可以使用运算符+=将字符串附加到string对象的末尾。
- string类的其他操作:头文件cstring(string.h)提供了这些函数。
strcpy()将字符串复制到字符数组中,strcat()将字符串附加到字符数组末尾。例:strcpy(charr1, charr2);strcat(charr1, charr2); - string类中size()函数跟strlen()函数相同。返回string类型的字符数。strlen()则是返回可见的字符数(不包括’\0’)。
- getline()函数与cin.getline()函数:getline()函数头文件是cstring.h,使用方式:string name; getline(cin, name);cin.getline()使用方式:char name[20];cin.getline(name, 20);
- 其他形式的字符串字面值:wchar_t、char16_t、char32_t。(char16_t、char32_t是C++11新增的类型)。C++分别使用前缀L、u、U表示:
wchar_t title[] = L”yu wen”;
char16_t name[] = u”xiao ming”;
char32_t car[] = U”xiao che”;
- 原始字符串(raw,C++11新增类型)。在原始字符串中,字符表示的就是自己,例:序列\n不表示换行符,而是表示两个常规字符(斜杆和n)。原始字符串将”(和)”用作定界符,并使用前缀R来标识原始字符串。例:cout << R"(“Hello World!\n”)";也可以自定义定界符,但是开头跟结尾相对应。
结构简介:
- 结构是一种比数组更灵活的数据格式,因为用一个结构可以存储多种类型的数据。结构也是类的基石。
- 结构是用户定义的类型,而结构的声明定义了这种类型的数据属性。定义了类型之后便可以创建这种类型的变量。创建结构包括两步:①定义结构描述(标记了能够存储在结构中的各种数据类型)。②按照描述创建结构变量(结构数据对象)。
- 结构类型:
struct Student {
int id;//学号
char name[20];//姓名
float score;//分数
int age;//年龄
};
用关键字struct声明,Student是是这种数据格式的名称。大括号中包含的是结构存储的数据类型的列表,每个列表项都是一条声明语句。列表中的每一项都被称为结构成员,因此Student结构共有4个成员。创建结构类型的变量:Student ming;Student hong;Student wang;由于ming的类型为Sturnde,因此可以使用成员运算符(.)来访问各个成员。例:ming.name = “xiaoming”;ming.id=1;访问类成员函数(cin.getline())的方式是从访问结构成员变量(ming.score)的方式衍生而来的。
4. 结构类型初始化:
Student hong = {
2,
"xiaoHong",
68.9f,
20
};
也可以省略等号。结构的初始化和数组一样,使用逗号分隔值列表,并将这些值用花括号括起。可以将每个结构成员看作是相应类型的变量。如果大括号内未包含任何东西,各个成员都将被设置成零。注:结构初始化时,不允许缩窄转换。
6. 结构声明的位置很重要。两种选择:①将申明放在main()函数中,紧跟在开始括号的后面(位于函数内部的声明被称为内部声明)。②将申明放到main()的前面(位于函数外面的声明被称为外部声明)。外部声明的可以被其后面的任何函数使用,而内部声明只能被该声明所属的函数使用。
7. C++中可以同时完成定义结构和创建结构变量的工作。
例:①
struct Student{
int id;
char name[20];
} ming, hong,gang;
struct {
int x;
int y;
} point;//不太推荐这种形式的结构
甚至可以按照这样的方式初始化:
struct Position {
int x;
int y;
} point{
0,
0
};
- C++中结构除了成员变量之外,还可以有成员函数(cin.getline())。
例:
struct Score{
double scoreA;
double scoreB;
double scoreC;
double average(){
return (scoreA + scoreB + scoreC) / 3;
}
}
- 结构数组:跟基本类型数组创建相同。例:Student student[60];
- 初始化结构数组:
Student s[2] = {
{1, “xiaoGang”},
{2, “xiaoHong”}
};
跟基本类型数组初始化一样。
10. 结构中的位字段:C++允许指定展通特定位数的结构成员,这使得创建与某个硬件设备上的寄存器对应的数据结构非常方便。字段的类型应为整型或者枚举,接下来是冒号,冒号后面是一个数字,指定了使用的位数。可以使用没有名称的字段来提供间距。每个成员都被称为位字段。例:
struct torgle_register{
unsignedint SN:4;
unsigned int :4;
bool goodIn:1;
bool goodTorgle : 1;
}
位字段通常用在低级编程中。
共用体(union):
- 共用体(union)是一种数据格式。能够存储不同的的数据类型,但只能同时存储其中的一种类型。也就是说结构(struct)可以同时存储int、long和double,共用体只能存储int、long或double。共用体的句法与结构相似,但含义不同。例:
union TestUnion{
int key;
long val;
long long v;
}; TestUnion un{10};共同体的长度为其最大成员长度。 - 共用体用途之一:当数据项使用两种或更多种格式(但不会同时使用)时,可节省空间。共用体常用于(但并非只能用于)节省内存。
- 匿名共用体:
struct Shop
{
int price;
union name {
char n[20];
} name_v;
union {//匿名共用体,没有名称,其成员变量将成为与相同地址处的变量。每次只有一个成员时当前地成员。
int key;
int val;
};
}; 初始化:Shop shop = {10, {“adsf”}, 10};
枚举(enum):
- enum枚举提供了另一种创建常量的方式,这种方式可以代替const。它还允许定义新类型,但必须按照严格的限制进行,enum句法与使用结构相似。例:
enum Color
{
red,
blue,
green,
black,
yellow,
orange
};Color成为新类型的名称,Color被称为枚举;red、blue、green作为符号常量,对应的整数值0-5,这些常量叫做枚举量。默认情况下,将整数赋值给枚举量,第一个枚举量的值为0,第二个枚举量的值是1,依次类推。也可以通过显式地指定整数值来覆盖默认值。 - 可以用枚举名来声明这种类型的变量:Color color;在不进行强制类型转换的情况下,只能将定义枚举时使用的枚举量赋给这种枚举的变量。color = red;(正确示例)color = 2000;(错误示例,Color只有6个可能的值)。
- 枚举只定义了赋值运算符,并没有定义算术运算符。枚举量时整型,可被提升为int类型,但int类型不能自动转换为枚举类型。如果int值是有效的,可以通过强制类型转换,将它赋值给枚举变量:color = Color(5);如果试图对一个不适当的值进行强制类型转换,其结果是不确定的:color = Color(1000);//undefined
- 设置枚举量的值:enum Type { one = 1, two = 2, four = 4, eight = 8 };
- 枚举的取值范围:每个枚举都有取值范围,通过强制类型转换,可以将取值范围中的任何整数值赋给枚举变量,即使这个值不是枚举值。例:
enum Bits { ont = 1, two = 2, four = 4, eight = 8 };
Bits myFlag = Bits(6);//是合法代码。6虽然不是枚举值,但是位于枚举定义的取值范围内。枚举量的最大值范围:枚举量的最大值的最小的2的幂,将它减去1,得到的就是取值范围的上限。例:enum BitsStep { first, second = 100,third };该枚举的取值上限为:2的幂中,比101大的最小值为128,因此取值范围的上限为127。枚举量的最小值范围:如果枚举量不小于0,则取值范围的下限为0,否则与上限方式相同,但加上负号,并且加1。