您的位置:首页 > 编程语言 > C语言/C++

【C++】第4章 复合类型 知识总结

2017-09-20 14:15 381 查看
《C++ Primer Plus》第4章知识点总结

内置的C++类型分两组:基本类型和复合类型。本文将介绍复合类型

数组

声明数组的通用格式:typeName arrayName[arraySize];

数组的初始化

(1)只有在定义数组时才能使用初始化,此后就不能使用了,也不能将一个数组赋给另一个数组

int cards[4]={3,6,7,10};//okay
int hand[4];//okay
hand[4]={5,6,7,9};//not allowed
hand=cards;//not allowed
然而可以使用下标分别给数组中的元素赋值
(2)如果指对数组的一部分进行初始化,则编译器将把其他元素设置为0.因此,将数组中所有的元素都初始化为0非常简单——只要显式地将第一个元素初始化为0,然后让编译器将其他元素都初始化为0即可:

long totals[500]={0};

(3)如果初始化为{1}而不是{0},则第一个元素设置为1,其他元素都被设置为0

C++11数组初始化方法

(1)初始化数组时,可省略等号(=):

double earning[4] {1.2e4,1.6e4,1.1e4,1.7e4};//okey with C++11

(2)可不再大括号内包含任何东西,这将所有元素都设置为零:

unsigned int counts[10]={};//all elements set to 0
float balances[100] {};//all elements set to 0

(3)列表初始化禁止缩窄转换:

long plifs[]={25,92,3.0};//not alowed,将浮点数转换为整型是缩窄操作,即使浮点数的小数点后面为零
char slifs[4] {'h','i',1122011,'\0'};//not allowed,1122011超出了char变量的取值范围(这里假设char变量的长度为8位)
char tlifs[4] {'h','i',112,'\0'};//allowed


字符串
C++处理字符串的方式有两种:一种来自C语言,常被称为C-风格字符串;另一种基于string类库的方法

C-风格字符串

首先来看这两个char数组

char dog[8]={'b','e','a','u','x','d','i','i'};
char cat[8]={'f','a','t','e','s','s','a','\0'};
他们都是char数组,但只有第二个数组是字符串。因为C-风格字符串具有一种特殊的性质:以空字符结尾,空字符被写作\0,其ASCII码为0,用来标记字符串的结尾。
例如,使用cout显示cat字符串,则将显示前7个字符,发现空字符后停止。但是,如果使用cout显示dog数组(它不是字符串),cout将打印出数组的8个字母,并接着将内存中随后的各个字节解释为要打印的字符,直到遇到空字符为止

当然,有一种更好的初始化方法——使用一个引号括起字符串即可

char bird[11]="Mr.Cheeps";
char fish[]="Bubbles";
用引号括起的字符串隐式的包括结尾的空字符,因此不用显示地包括它
另外,空字符 \0可用于截断字符串

例如:name2[3]='\0',表示让字符串name2的第3位变为\0,因此cout在显示时,只显示name2的前3个字符,后面的字符将不被显示

拼接字符串常量

任何两个由空白(空格、制表符和换行符)分割的字符串常量都将自动拼接成一个。因此,下面所有的输出语句都是等效的

cout<<"I'd give my right arm to be"" a great violinist.\n";
cout<<"I'd give my right arm to be a great violinist.\n";
cout<<"I'd give my right ar"
"m to be a great violinist.\n";
注意:拼接时不会在被连接的字符串之间添加控个,第二个字符串的第一个字符将紧跟在第一个字符串的最后一个字符(不考虑\0)后面。第一个字符串中的\0字符将被第二个字符串的第一个字符取代

strlen():返回存储在数组中的字符串的长度,而不是数组本身的长度。另外,它只计算课检的字符,而不把字符串计算在内

getline():面向行的输入,读取整行,它使用通过回车键输入的换行符来确定输入结尾。而调用这种方法,可以使用 cin.getline(),例如:cin.getline(name,20); 该函数有两个参数,第一个参数是用来存储输入行的数组的名称,第二个参数是要读取的字符数,如果这个参数为20,则函数最多读取19个字符,余下的空间用于存储自动在结尾处添加的空字符。它通过换行符来确定行尾,但不保存换行符。相反,在存储字符串时,它用空字符来替代换行符。

对字符串使用的方法:getline(cin,str);需要由string头文件

get():面向行的输入,工作方式与getline()类似,接受的参数都相同,并且都读取到行尾,但get并不再读取并丢弃换行符,而是将其留在输入队列中。假设我们连续调用get()

cin.get(name,ArSize);
cin.get(dessert,Arsize);
由于第一次调用后,换行符将留在输入队列中,因此第二次调用时看到的第一个字符便是换行符
不过get()有另一种变体。使用不带任何参数的cin.get()调用可读取下一个字符(即使是换行符),因此可以用它来处理换行符,为读取下一行输入做好准备。

cin.get(name,ArSize);
cin.get();
cin.get(dessert,Arsize);
cin.clear():当getline()或get()读取空行时,最初的做法是下一条输入语句将在前一条getline()或get()结束读取的位置开始读取;但当前的做法是,当get()读取空行后将设置失效位。这意味着接下来的输入将被阻断,但可以用这条命令来恢复输入

混合输入字符串和数字

这里只做一下文字的阐述,例子有点长我就偷一下懒了

当cin读取一个数字时,将回车键生成的换行符留在了输入队列中。后面的cin.getline()看到换行符后,将认为是一个空行,并将空字符串赋给原本用作存储字符串的数组。

解决方法是在读取地址之前先读取并丢弃换行符。比如在cin读取命令的下一行加上cin.get();

String类简介

要使用string,必须在程序中包含头文件string。string类位于名称空间std中,因此必须提供一条using编译指令。

string对象的使用

(1)可以使用C风格字符串来初始化string对象

(2)可以使用cin来将键盘输入存储到string对象中

(3)可以使用cout来显示string对象

(4)可以使用数组表示法来访问存储在string对象中的字符

头文件cstring提供的函数

strcpy(charr1,charr2);——将字符串复制到字符数组中

strcat(charr1,charr2);——将字符串charr2附加到字符数组charr1末尾

结构简介

#include <iostream>
using namespace std;
struct/*这是关键字*/ inflatable/*标记称为新类型的名称*/
{
    //下面这些是结构的成员
    char name[20];
    float volume;
    double price;
};//注意这里有个分号,极其容易漏
int main()
{
    //第一种初始化方式
    inflatable guest=
    {
        "Glorious",
        1.88,
        29.99
    };
    //第二种初始化方式
    inflatable duck={"Daphne",0.12,9.98};
    //第三种初始化方式,这个表示每个成员都被设置为0
    inflatable mayor={};
    return 0;
}

以下是另外三种结构体的创建格式

//第1种,直接在创建结构体的时候创建变量
struct perk1
{
int key_number;
char car[12];
}mr_smith,ms_jones;
//第2种,创建并初始化变量
struct perk2
{
int key_number;
char car[12];
}mr_glitz=
{
7,
"Packard"
};
//第3种,创建一个名为position的结构变量
//可以使用成员运算符来访问它的成员(如position.x)
//但这种类型没有名称,因此以后无法创建这种类型的变量
struct
{
int x;
int y;
}position;

(1)结构体声明要在使用之前,这类似于函数定义

(2)放在局部函数中的结构体定义,在其他函数体内将不可见

(3)初始化方式建议写到一行

(4)初始化时如果不指定值,将默认为0(对应的,字符串为空,指针为NULL)

(5)结构体成员的类型可以任意,即可以将类对象作为成员,也可以将结构体作为成员,或将数组作为成员

结构数组
inflatable guest[2]=
{
{"Bambi",0.5,21.99},
{"Godzilla",2000,565.99}
};


结构中的位字段

C++允许指定占用特定位数的结构成员,这使得创建与某个硬件设备上的寄存器对应的数据结构非常方便,例如

struct torgle_register
{
unsigned int SN:4;//4 bits for SN value
unsigned int :4;//4 bits unused
bool goodIn:1;//valid input (1 bit)
bool goodTorgle:1;//successful torgling
};


共用体
共用体是一种数据结构,它能存储不同的数据类型,但只能同时存储其中的一种类型,例如

union one4all
{
int int_val;
long long_val;
double double_val;
};
这个结构体表示,可以使用one4all变量来存储int、long或double,注意是“或”,也就是说,如果存取了int,就不能存取long和double

枚举

enum spectrum{red,orange,yellow,green,blue};
这条语句让spectrum称为新的类型的名称:spectrum被称为枚举,就像struct变量被称为结构一样
在默认情况下,将整数值赋给枚举量,第一个枚举量的值为0,第二个枚举量的值为1,以此类推。对于枚举,只定义了赋值运算,没有定义算术运算。以下是几点关于赋值的注意

spectrum band;
band=blue;//可以
band=2000;//不可以,超出范围

band=orange;
++band;//不可以,枚举没有定义算术
band=orange+red;//同上,不可以

int color=blue;//可以,此时spectrum类型转换为int类型
color=3+red;//这种情况下,red和color都变为int类型,不再是枚举

band=3;//不可以这样赋值
band=spectrum(3);//可以这样赋值
band=spectrum(2000);//超出了范围

设置枚举的值
//使用赋值运算符来显示地设置枚举量的值,但必须是整数
enum bits{one=1,two=2,four=4,eight=8};
//也可以值显示地定义其中一些枚举量
//这里,first在默认情况下为0,后面没有被初始化的枚举量的值将比前面的枚举量大1
//因此third的枚举量为101
enum bigstep{first,second=100,third};
//也可以创建多个值相同的枚举量
enum{zero,null=0,one,numero_uno=1};


枚举的取值范围
首先,要找出上限,需要直到枚举量的最大值。找到大于这个最大值的、最小的2的幂,将它减去1,得到的便是取值范围的上限。例如前面bigstep的最大值枚举值是101,在2的幂中,比这个数大的最小值为128,因此取值范围的上限为127。

要计算下限,需要直到枚举量的最小值。如果它不小于0,则取值范围的下限为0;否则,采用与寻找上限方法相同的方式,但加上符号。例如,如果最小的枚举量为-6,而比它小的、最大的2的幂是-8,因此下限为-7

指针和自由存储空间

指针是一个变量,其存储的是值的地址,而不是值本身。*运算符被称为间接值或解除引用运算符,将其应用于指针,可以得到该地址所存储的值

对变量应用地址运算符(&),就可以得到变量的位置

声明和初始化指针

int* ptr;//声明一个指针ptr
int* p1,p2;//注意这种情况下,p1为指针,p2为整型。对每个指针变量名,都需要使用一个*
//下面的语句将pt(而不是*pt)的值设置为&higgens
int higgens=5;
int *pt=&higgens;

指针和数字
指针不是整数,而是描述的位置,因此不能简单的将整数赋给指针

使用new来分配内存

变量是在编译时分配的有名称的内存,而指针只是为可以通过名称直接访问的内存提供了一个别名。指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值。在这种情况下,只能通过指针来访问内存。在C++用,常用new运算符来分配内存

为一个数据对象获得并指定分配内存的通用格式如下:

typeName* pointer_name=new typeName;

需要在两个地方指定数据类型:用来指定需要什么样的内存和用来声明合适的指针

使用delete释放内存

当需要内存时,可以使用new来请求。而在使用完内存后,delete运算符可以将内存归还给内存池,这是通向最有效地使用内存的关键一步。

int *ps=new int;
...
delete ps;

注意,使用delete的关键在于,将它用于new分配的内存。这并不意味着要使用用于new的指针,而是用于new的地址

使用new创建动态数组

int* psom=new int[10];
delete[] psom;
请注意,delete和指针之间的方括号,如果使用new时,不带方括号,则使用delete时,也不应带方括号。如果使用new时带方括号,则使用delete时也应带方括号。

使用new和delete时,应遵守一下规则

(1)不要使用delete来释放不是new分配的内存

(2)不要使用delete释放同一个内存块两次

(3)如果使用new[]为数组分配内存,则应使用delete[]来释放

(4)如果使用new为一个实体分配内存,则应使用delete(没有方括号)来释放

(5)对空指针应用delete时安全的

使用动态数组

int* psome=new int[10];
这句语句创建指针psome,它指向包含10个int值的内存块中的第1个元素

下面对指针进行一个小结

(1)声明指针的格式:typeName* pointerName;

(2)给指针赋值

double* pn;
double* pa;
char* pc;
double bubble=3.2;
pn=&bubble;//pn为bubble的地址
pc=new char;//pc为new的内存的地址
pa=new double[30];//pa为new的数组空间的第一个数的地址
(3)对指针解除引用
对指针解除引用意味着获得指针指向的值。对指针应用解除引用或间接值运算符(*)来解除引用。例如上面的例子,pn是指向bubble的指针,则*pn时指向bubble的值,即3.2

(4)区分指针和指针所指向的值

如果pt时指向int的指针,则*pt不是指向int的指针,而是完全等同于一个int类型的变量。pt才是指针

(5)数组名

在多数情况下,c++将数组名视为数组的第一个元素的地址

一种例外情况时,将sizeof运算符用于数组名用时,此时将返回整个数组的长度(单位为字节)

(6)指针算数

C++允许将指针和整数相加。加1的结果等于原来的地址值加上指向的对象占用的总字节数

(7)数组的动态联编和静态联编

使用数组声明来创建数组时,将采用静态联编,即数组的长度在编译时设置

使用new[]运算符创建数组时,将采用动态联编(动态数组)即将在运行时为数组分配空间,其长度也将在运行时设置。使用完这种数组后,应使用delete[]释放其占用的内存

(8)数组表示法和指针表示法

使用方括号数组表示法等同于指针解除引用

使用new创建动态结构

inflatable* ps=new inflatable;
创建容易访问难。创建动态结构时,不能将成员运算符句点用于结构名,因为这种结构没有名称,只是知道它的地址,所以c++专门提供了一个运算符:箭头成员运算符(->)。例如,如果ps指向一个inflatable结构,则ps->price时被指向的结构的price成员

自动存储、静态存储和动态存储

自动存储:在函数内部定义的常规变量使用自动存储空间,被称为自动变量,这意味着它们在所属的函数被调用时自动产生,在该函数结束时消亡

静态存储:静态存储时整个程序执行期间都存在的存储方式。使变量称为静态的方式有两种:一种是在函数外面定义它;另一种是在声明变量时使用关键字static

动态存储:new和delete运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理了一个内存池,这在C++中被称为自由存储空间或堆。该内存池同用于静态变量和自动变量的内存是分开的。new和delete让你能够在一个函数中分配内存,而在另一个函数中释放它。因此,数据的生命周期不完全受程序或函数的生存时间控制。与使用常规变量相比,使用new和delete让程序员对程序如何使用内存有更大的控制权。然而,内存管理也更复杂了。

数组的替代品

vector

vector类似与string类,也是一种动态数组。它时使用new创建动态数组的替代品。vector自动使用new和delete来管理内存,以下是几点vector的实用知识

(1)首先,要使用vector对象,必须包含头文件vector

(2)其次,vector包含在名称空间std中

(3)模版使用不同的语法来指出它存储的数据模型

(4)vector类使用不同的语法来指定元素数

(5)声明格式为:vector<typeName> vt(n_elem);  //命名一个大小为n_elem的类型为typeName的vt数组

array

array位于名称空间std中,和数组一样,array对象的长度时固定的,也使用栈(静态内存分配),而不是自由存储区,因此其效率与数组相同,但更方便、更安全。要创建array对象,需要包含头文件array。

声明格式为:array<typeName,n_elem> arr;  //命名一个大小为n_elem的类型为typeName的arr数组

这个声明创建一个名为arr的array对象,它包括n_elem个类型为typename的元素

以下有几点要说明的

(1)首先,注意到无论是数组、vector对象还是array对象,都可使用标准数表示法来访问各个元素

(2)其次,从地址可知,array对象和数组存储在相同的内存区域(即栈)中,而vector对象存储在另一个区域(自由存储区或堆)中

(3)注意到可以将一个array对象赋给另一个array对象;而对于数组,必须逐元素复制数据

(4)仍然没有在直接访问方式上确保越界
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: