以C++ Primer Plus为参考书籍,自身归纳知识点,加深记忆。
第四章 复合类型
4.1 数组
数组(array)是一种数据格式,存储多个同类型的值。声明数组应指出数组中元素的类型,元素数量以及数组名称。
声明格式:TypeName ArrayName[ArraySize] ,其中ArraySize必须为整型常量或者const值或常量表达式,即所有的值在编译时已知,不能为变量。
数组的初始化规则:① 只有定义数组才能使用初始化,不能将一个数组赋给另一个数组,但可以用数组下标分别赋值;
② 初始化数组时可以进行部分初始化,其余部分编译器默认为0;
③ C++11 新方法:初始化数组,可以省略 = 号;int ary[3] {1,2,3};
④ C++11 新方法:大括号内可为空,则所有元素均为0;int ary[4] {};//ary数组中元素初始化为0.
⑤ C++11 新方法:列表初始化禁止缩窄转换。long ary[3] {25,30,32.5};//ary[2]为float类型,浮点数转为整型是缩窄操作,小数部分的精度会丧失。
4.2 字符串
字符串是存储在内存中连续字节中的一系列字符。传统C语言中字符串末尾以空字符 ‘\0’ 结尾,但有一种更好的初始化字符串的方法,那就是直接用引号括起来字符串,这种字符串成为字符串常量(string constant)。char food[10] = "bread";
引号括起的字符串隐式包括结尾的 ‘\0’ ,因此不显式包括。
字符串的输入
iostream中的cin会将键盘输入中的空格,制表符,换行符以分界线结束位置,因此在输入名字时会导致“Smith Young”只录入“Smith”一个单词。在istream中提供了面向行的类成员函数。
4.2.1 getline()
getline()读取整行,通过回车键入的换行符确定输入结尾,调用方式为 cin.geiline(arrayname,size) ,getline读取的字符数为 size-1,剩下的空间存储一个 ‘\0’。
#include<iostream>
int main()
{
using namespace std;
const int size = 20;
char name[size];
cout >> "输入英文名字: ";
cin.getline(name,size);
cout << "你的英文名字是: " << name << endl;
return 0;
}
4.2.2 get()
get()接受的参数与getline()相同,同样读取到结尾,但是get()不丢弃换行符,而是将其保留在输入队列中。因此在使用get()时应考虑在键入回车时残留下的换行符,通常的解决方式为
//接上部分代码
cout >> "输入英文名字: ";
cin.get(name,size);
cin.get();//读取键入后的换行符以方便下次的键入读取
另外一种方式是将两个类成员函数拼接起来,效果一样:
cin.get(name,size).get();//cin.get()返回一个cin对象,该对象又被用来调用get(),故可以这样拼接。
//cin.getline(name1,size).getline(name2,size) 则是读取两行分别读入到name1和name2数组中,其效果与调用两次getline()相当。
4.3 string类
要使用string类,必须包含头文件string,string类位于名称空间std中,因此需要一条using编译指令,或者std::string引用。
声明: string ArrayName,string对象和字符数组的区别就是可以将string对象声明为简单变量,而不是数组,并且类设计让程序自动处理string的大小,自动调整string的长度。string str = "amazing" ;//或采取C++11 string str = {"amazing"};“=”可以省略。
4.3.1 赋值、拼接与附加
① string对象可以赋给另一个string对象
char food1[10];
char food2[10] = "bread";
food1 = food2; //编译不可通过,字符串间不能直接赋值
string drink1 = "milk";
string drink2;
drink2 = drink1; //string对象的特殊性
② string简化了字符串合并,可以直接用 + 将两个string对象合并,还可以用 += 附加到对象末尾
string str1 = "talking";
string str2 = " to the moon";
string str3;
str3 = str1 + str2; //str3 = "talking to the moon"
str1 += str2; //str1 = "talking to the moon"
4.3.2 string的其他事项
在C++新增string之前,使用头文件cstring来完成拼接等操作。
使用函数strcpy()实现字符串的复制,使用函数strcat()实现字符串附加在末尾:
strcpy(str1,str2) //将str2复制到str1中
strcat(str1,str2) //将str2接到str1的末尾
但strcat,strcpy存在安全隐患,由于str1,str2的字符数组大小已定,因此在拼接,复制过程中容易出现str1的大小过小,导致无法完整存储信息的危险,因此提供了strncpy()与strncat()函数,它们接受第三个参数为目标数组最大允许长度,但还是不如string类自动调整大小的功能。
4.3.3 string类的I/O
#include<iostream>
#include<string>
int main ()
{
using namespace std;
char food[20];
string drink;
cout << "Please input food name:";
cin.getline(food,20);
cout << "Please input drink name:";
getline(cin,drink);
return 0;
}
在引入string之前,C++就有istream类,因此没有考虑到string类型,所以没有处理string对象的方法,因此字符串常量采取cin.getline(),而string对象采取getline()。
4.3.4 其他形式的字符串字面值
① 类型wchar_t,C++11还新增了char16_t 和 char32_t,分别用前缀L、u和U表示
wchar_t food[] = L"Sssnjk JHbuihbi";
char16_t food2[] = u"Sssnjk JHbuihbi";
char32_t food3[] = U"ssnjk JHbui";
② C++11 还新增原始(raw)字符串,在原始字符串中,字符表示的就是自己,比如转义序列 \n不再是换行符而是独立的 \ 和 n。原始字面符将 "( 和 )"用作界定符,并在前标R标识。
4.4 结构简介
结构是一种比数组更灵活的数据格式,同一个结构可以存储多种类型的数据。
定义结构:
//定义结构
struct fish
{
char name[20];
int num;
float weight;
}; //结尾的分号不能忘
//创建结构变量
fish Fish; //C++ 允许声明结构变量省略关键词struct,
结构的初始化
fish Fish =
{
"carp",3,4.2
};//使用逗号分隔,并用花括号括起,C++11中(=)可选并且不允许缩窄转换,而且花括号可以为空。
结构内可以使用string对象,但是必须让结构访问名称空间std,可以将using放在结构声明之前或者采取 std::string name。
4.4.1 结构数组
fish Fishs[20]; 此时Fishs是一个fish数组,其中的每个元素都是fish的对象,Fishs本身是个数组,而不是结构
结构数组的初始化:
fish Fishs[2] =
{
{"carp",2,1.9},
{"salmon",4,6.7}
};
4.5 共用体(联合)
共用体(union)是一种数据形式,能够存储不同的数据类型,但只能同时存储一种类型,语法与结构类似。
union world
{
int cnt;
double money;
long maximum;
};//共用体的长度为最大成员的长度
4.6 枚举
enum color {red, orange, yellow, green, blue, violet, indigo};
red、orange、yellow等作为符号常量,对应整数值0~6,常量叫枚举量,枚举量为整型,可被提升为int型。
4.7 指针与自由存储空间
指针是一个变量,其存储的是值的地址,而不是值本身。 *运算符成为解引用运算符,应用于指针。
int cnt = 8;
int *pcnt;
pcnt = &cnt;//对变量cnt取址,并赋予pcnt
int *ptr = &cnt/*还有简洁写法*/
❗ 指针的声明需要与*联合使用,不能省略
int *ptr,str;//此时声明了一个str变量与一个指向int型的指针ptr
4.7.1 分配内存
C语言中分配内存采用malloc()函数,C++也可以采用这个,同时还有更好的办法 — new运算符。
TypeName *pointer_name = new typename
#include<iostream>
int main()
{
using namespace std;
int cnt = 200;
int *ptr = new int;
*ptr = 200;
cout << "cnt = " << cnt <<" location cnt = " << &cnt << endl; //cnt = 200 location cnt = 0x7ffe1ed8b8c4
cout << "*ptr = " << *ptr << " location ptr = " << ptr <<endl;//*ptr = 200 location ptr = 0x108d010
cout << "sizeof cnt = " << sizeof(cnt) << endl;//sizeof cnt = 4
cout << "sizeof *ptr = " << sizeof(*ptr) << endl;//sizeof *ptr = 4
cout << "sizeof ptr = " << sizeof(ptr) <<endl;//sizeof ptr = 8 ,64位机器的编译器指针大小为8byte,32位则为4byte
return 0;
}
4.7.2 释放内存
C语言中与malloc()对应的释放内存函数为free(),C++中则用delete()。
int *ptr = new int;
delete ptr;
注意:delete必须指向new的指针对应的内存,只会释放ptr指向的内存但不会把指针ptr本身删除,delete与new配对使用,否则会导致内存泄漏,被分配的内存再也无法使用,如果内存泄露严重,程序将由于不断寻找更多内存而终止。delete不能用于释放声明变量所获的内存。
4.7.3 new 与 动态数组
数组分配内存的格式:
typename *pointername = new typenamep[num];
数组释放内存格式:
delete [] ponitername;
int *ptr = new int[4];
ptr[3] = 6;
ptr[2] = 9;
ptr[1] = 12;
ptr[0] = 40;
cout << "ptr[2]= " << ptr[3] <<endl;//ptr[2] = 9
cout << "ptr[2]= " << *(ptr+2) << endl;//ptr[2] = 9
delete []ptr;
return 0;
上述代码中,可以用数组表示法表示数组中元素的值 cout << "ptr[2]= " << ptr[3] <<endl;
,也可以用指针表示法表示cout << "ptr[2]= " << *(ptr+2) << endl;
4.7.4 new 与 结构
new用于结构首先需要创建一个结构,然后访问其成员
#include<iostream>
struct fish
{
char name[20];
int num;
};
int main()
{
using namespace std;
fish *ptr = new fish;//new创建内存
cout<<"Enter name of fish: ";
cin.getline(ptr->name,20);//指针表示法表示结构
cout<<"Enter num of fish: ";
cin>>(*ptr).num;//常规结构表示法,*ptr是fish结构本身
cout<<"NAME: "<<(*ptr).name<<endl;
cout<<"NUM: "<<ptr->num<<endl;
delete ptr;//释放结构内存
return 0;
}
4.7.4.1 new与结构数组
structname *pointname = new structname[num]
struct fish
{
char name[20];
int num;
}
fish *pfish = new fish[2];//new结构数组
4.8 数组的替代品
4.8.1 模板类vector
模板类vector类似于string类,自动完成new与delete的内存管理,是new创建动态数组的替代品。要使用vector对象,头文件包含vector并且存在于名称空间std中,因此使用using编译或者using声明或者std::vector。
声明格式:
vector name(num); num可以是整型常量或者整型变量。
4.8.2 模板类array(C++11)
创建array对象需要array头文件,以及名称空间std,与数组一样,array对象长度固定,使用栈(静态内存分配),效率与数组相同,但array可以对数组进行直接赋值,arr1=arr2;
声明格式:
array<typename,num>name;,num只能为整型常量。
#include<iostream>
#include<vector>
#include<array>
int main()
{
using namespace std;
double cary[4] = {2.3,6.5,7.2,1.6};//传统C语言的数组的初始化
vector<double>vary[4];
vary[0] ={6.1};//vector对象的初始化
vary[1] ={5.9};
vary[2] ={7.4};
vary[3] ={8.3};
array<double,4>aary = {1.4,5.8,4.9,6.3};
array<double,4>bary;
bary = aary;//array允许数组的复制
cout<<"aary[1~4]: "<< aary[0] <<" "<<aary[1] <<" "<<aary[2]<<" "<<aary[3]<<endl;
cout<<"bary[1~4]: "<< bary[0] <<" "<<bary[1] <<" "<<bary[2]<<" "<<bary[3]<<endl;
return 0;
}