C++常用数据结构

1.数组

  • 数组(array)是一种数据格式,能够存储多个同类型的值。

  • 数组声明方法:typeName arrayName[arraySize](arraySize必须是整数)

例如:short months[12],其含义指创建了一个名为months的short类型的数组,数组内有12个元素。其中,数组months中的每一个元素,都可以单独访问。如:months[0]months[1]、……、months[11]可以一一访问数组中的元素。(C++数组中从0开始编号)

数组初始化规则

  1. 数组初始化只有在声明数组时使用,也不可以将一个数组赋给另一个数组。
int cards[4] = {3,6,8,10};	//正确初始化数组

int hard[4];
hard[4] = {3,6,8,10};	//错误使用

hard[4] = cards[4];	//错误使用
  1. 初始化数组时,可以提供少于数组arraySize的元素数目。
float hotelTips[5] = {5.0,2,5};	//指数组中第一、第二个元素值为5.0、2.5,其余元素为0
  1. 初始化数组时,[ ]中的arraySize可以为空,C++编译器能自动计算元素个数
short things[] = {1,5,3,8};	//指数组things含有4个元素

这种初始化方法容易丢失元素,最好输入arraySize

  1. C++数组初始化的方法
  • 可以省去=
  • {}可以不包含元素,意为所有元素为0
  • 初始化时,禁止缩窄转换,即浮点数不可以转换为整型

2.字符串

  • 字符串是存储在内存的连续字节中的一系列字符。(C++有两字符串处理方式:C-style string和string类)

  • 将数组初识化为字符串有两种方式:

//第一种方法:
//C-style string以空字符结尾(null character),被写作\0,ASCII码为0,不以空字符结尾的不是字符串
char dog[5] = {'b','e','u','a','e'};	//没有以空字符'\0'结尾,不是字符串
char cat[5] = {'b','e','u','a','\0'}; 	// 是字符串

//第二种方法:
char bird[11] = "Mr. Cheeps"; 	
//其中,11个元素包含空字符,若arraySize大于元素数目,则其余未输入字符算作空字符'\0'

常用第二种方法来将数组初始化为字符串。字符串常量用双引号,字符常量用单引号,不能互换。

char shirt_size = 'S';	//指将S的ASCII值83赋给shirt_size
//"S"不是字符常量,是字符串常量,表示两个字符(字符S和\0)
char shirt_size = "S"	//这是错误的表达方式

字符串常量拼接

当字符串很长时,可以将两个引号括起的字符串合并成一个。

//下面两个语句时等效的,且第一个字符串中的空字符\0,会被第二个字符串的第一个字符取代
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";

字符串输入

两次输入字符串产生的问题:

#include <iostream>
int main()
{
    using namespace std;
    const int ArSize = 20;
    char name[ArSize];
    cahr dessert[ArSize];
    
    cout << "Enter your name:\n";
    cin >> name;
    cout << "Enter your favorite dessert:\n";
    cin >> dessert;
    cout << "I have some delicious " << dessert;
    cout << " for you, " << name << ".\n";
    return 0;
}

//运行结果为:
Enter your name:
Alistair Dreeb
Enter your favorite dessert:
I have some delicious Dreeb for you, Alistair.

上述代码中,在第一次cin >> name后,程序便显示结果,并立即显示最后一行。这是因为,cin使用空白(空格、制表符和换行符)来确定字符串的结束位置,所以cin在获取字符串数组时只读取一个单词并自动在结尾添加空字符。第二次cin >> dessert时,程序发现输入的第二个单词Dreeb,便读取了它。

解决方法: 使用getline()或get()函数

  • getline()通过回车键(Enter)输入的换行符来确定结尾。调用方式:cin.getline(arrayName,arraySize)
char name[10];
cout << "Enter your name: ";
cin.getline(name,10);	//其中,10个元素包含空字符\0,故最多输入9个字符
  • get()与getline()的调用方式类似,但get()不再读取并丢失换行符,而是将其保留在输入队列中。故两次使用时,会导致第二次读取的第一个字符时换行符,而没有任何可读取的内容。
cin.get(name,ArSize);	//第一次读取正常
cin.get(dessert,ArSize);	//第二次读取错误,无可读取内容

//可通过下述方法解决
cin.get(name,ArSize);
cin.get();
cin.get(dessert,ArSize);
或者
cin.get(name,ArSize).get();	//即有多次cin输入时,在结尾继续调用一个get()
cin.get(dessert,ArSize);

输入数字和字符串产生的问题:

#include <iostream>
int main()
{
    using namespace std;
    cout << "What year was your house built?\n";
    int year;
    cin >> year;
    cout << "What is its street address?\n";
    chat address[80];
    cin.getline(address,80);
    cout << "Year built: " << year << endl;
    cout << "Address: " << address << endl;
    cout << "Done!\n";
    return 0;
}

//运行结果
What year was your house built?
1966
What is its street address?
Year built: 1966
Adress
Done!

上述代码中,地址(address)根本没输入机会,程序便运行完了。这是因为,cin读取年份时,将回车键生成的换行符留在了输入队列。故后面的cin.get(address,80)首先读取到换行符。

解决方法:

cin >> year;
cin.get();
或者
(cin >> year).get();

字符串中用到的函数

  • strlen函数

strlen函数可以返回存储在数组中的字符串的长度,并非数组长度。strlen函数只计算可见字符,不把空字符计算在内。因为strlen()函数是C语言库函数中的,所以使用前要包含标准头文件cstring

char name[5] = "abcd";
cout << "Your name has " << strlen(name) << " letters.\n"	
// 输出结果是Your name has 4 letters.
// arraySize = strlen(array) + 1

3.String类

  • string类使用前,必须在程序中包含头文件string,并string类位于名称空间std中,所以要提供一条using编译指令(或者std::string)。
  • string对象和字符数组主要区别是可以将string对象声明为简单变量,而不是数组
string str1;	//创建一个string对象或者说string类型的变量
string str2 = "panther";	//初始化字符串

字符串初始化、赋值、拼接和附加

  • 字符串初始化
string first_date = {"The Bread Bowl"};
string second_date = {"Hank's Fine Eats"};	
//string类型的变量不用加[]
  • 赋值

不能将一个数组赋给另一个数组,但可以将一个string对象赋给另一个string对象

char char1[20];
char char2[20] = "jaguar";
string str1;
strign str2 = "panther";
char1 = char2;	//错误使用
str1 = str2;	//正确使用
  • 拼接和附加
string str3;
str3 = str1 + str2;
str1 += str2;	//等效于str1 = str1 + str2

字符串中常用函数

  • strcpy()函数

strcpy函数可以将字符串复制到字符数组中。

strcpy(charr1,charr2);	//将charr2中字符串复制到charr1中
  • strcat()函数

strcat()函数将字符串附加到字符数组末尾。(concatenate,连接)

strcat(charr1,charr2);	//将charr2附加到charr1末尾
  • strncpy()和strncat()函数

当使用strcpy()和strcat()后,有可能原数组内存不够,导致覆盖相邻的内存,从而引起程序终止或者数据被破坏。而strncpy()和strncat()能自动调整大小。

  • size()函数
string str = "abcde";
int len1 = str.size();	//意为将str对象的字符数赋给len1

字符串输入

将一行输入读取到数组中的代码:

cin.getline(charr,20);

将一行输入读取到string对象中的代码:

getline(cin,str);

在string对象中未使用句点表示法,说明getline()不是类方法。以cin为参数,指出到哪里去查找输入,并且未指出字符串长度参数,是因为string对象能更具字符串的长度自动调整大小。

原始字符串

原始(Raw)字符串中,字符表示的就是自己,即没有转义序列一说法。例如:\n不表示换行符,表示两个常规字符——斜杠和n,因此可以显现出来。原始字符串将“(和)”用作定界符,并用前缀R来标识原始字符串。

cout << R"(Jim "King" Tutt uses "\n" instead of endl.)” << '\n';
//显示结果为Jim "King" Tutt uses "\n" instead of endl.

如果原始字符串中包含)”的话,可以在“和(中加入其他字符。

cout << R"+*("(Who wouldn't it?)", She whispered.)*+"" << endl;

4.结构

结构是一种能够存储多种数据类型的数据格式。定义结构方式:定义结构描述和描述创建结构变量。

struct inflatable
{
  char name[20];	//结构中的成员,一条声明语句,注意冒号“;”
  float volume;
  double price;
};			//该过程是声明结构这一数据类型,故也是一条声明语句,结尾要加上";"

上述代码中,inflatable是标识符,意为含上述代码中数据格式的新类型。定义结构可以放在函数内部,也可以在外部定义。区别就是函数内部定义,该结构只能在该函数中使用;外部定义,则整个程序都可以使用。

定义完结构后,就可以创建这种类型的变量了。

inflatable hat;
inflatable woopie_cushion;

其中,hat和woopie_cushion的类型为infalatable,因此可以用成员运算符(.)来访问其中的成员。例如:hat.volume、hat.price、woopie_cushion.price等就是结构中的成员,其的访问成员与float、double等变量没有区别。

结构初始化

inflatable duck = 	//等于号可以省略
{
"Glorious Gloria",	//因数据类型是字符串数组,故要用到双引号
1.88,			//因不是语句,故结尾不用冒号,用顿号
29.99			//结构成员中数据类型的赋值,与常规的整型、浮点型复制没有区别
};			// 初始化的过程是一个语句,结尾要加上冒号

可以使用赋值运算符(=)将一个结构赋给另一个同类型的结构

infalable choice;
choice = duck;	//上个程序中新类型duck

结构数组

标识符inflatable也可以包含一个数组,也就是可以创建元素为结构的数组。

inflatable gifts[2];	//创建了一个是新类型inflatable的gifts[2]数组

其中,gifts是一个Inflatable数组,内部的每一个元素(如gifts[0]、gifts[1]等)都是inflatable对象。

初识化结构数组:

inflatable gifts[2] =
{
	{"Bambi",0.5,21.99},
	{"Godzilla",2000,565.99}
};

上述代码中,可以使用gifts[0].volume、gifts[1].price来访问gifts数组元素中的元素。

结构中的位字段

结构中的位字段是指定占用特定位数的结构成员。字段类型为整型或枚举,接下来是冒号,冒号后面是一个数字,用来指定使用的位数。每个成员占有特定的位数,称为位字段。

struct torgle_register =
{
	unsigned int SN : 4;	//意为SN变量占4bits
	unsigned int : 4;	//使用没有名称的字段来提供间距
	bool goodIn : 1;
	bool goodTorgle : 1;
};

共用体

共用体(union)是一种数据格式,能够存储不同的数据类型,但只能同时存储其中的一种类型。例如:结构可以同时存储int、long和double,共用体只能存储int、long或double。用法与结构类似。

union one4all
{
	int int_val;
	long long_val;
	double doubel_val;
};
//可以使用one4all变量来储存int、long或doubel,前提是在不同的时间使用
one4all pail;
pail.int_val = 15;
cout << pail.int_val;
pail.double_val = 1.38;
cout << pail.double_val;

因为共用体每次只能存储一个值,因此必须有足够的空间来存储最大的成员。

共用体的使用

当数据项使用两种或更多格式(但不会同时使用)时,可节省空间。例如:一些商品的ID为整数,而另一些为字符串时,可以进行下面的方法:

struct widget =
{
char brand[20];
int type;
union id
{
	long id_num;
	char id_char[20];
}id_val;	//定义共用体的同时,创建了一个共用体类型的id_val变量
};
...
widget prize;
...
if (prize.type == 1)
	cin >> prize.id_val.id_num;
else
	cin >> prize.id_val.id_char;

匿名共用体没有名称,其成员将成为位于相同地址处的变量。

struct widget =
{
char brand[20];
int type;
union id
{
	long id_num;
	char id_char[20];
};	//定义共用体的同时,创建了一个共用体类型的id_val变量
};
...
widget prize;
...
if (prize.type == 1)
	cin >> prize.d_num;
else
	cin >> prize.id_char;
//id_num和id_char被视为prize的两个成员,它们地址相同

5.枚举

C++的enum工具提供了另一种创建符号常量的方式,可以用来替代const。定义 枚举方式如下:

enum spectrum {red,orange,yellow,green,blue,violet,indigo,ultraviolet};
  • spectrum被称为枚举(enumeration),是一种新类型(类似于int、long、double等类型),就像struct变量被称为结构一样。
  • red、orange、yellow等作为符号常量,依次对应整数0~7,称为枚举量。(默认情况下第一个枚举量为0,第二个枚举量为1)

创建枚举变量

创建枚举变量

spectrum band;	//创建一个spectrum类型的枚举变量
band = blue;	//意为将bule对应的4赋给band
band = 2000;	//错误,只能将定义枚举时的枚举常量赋给枚举变量,所以受spectrum影响band只有8个可能值
band = spectrum(3);	//等同于将3赋给band,前提是()内的int值是在定义的枚举量中才行

对于枚举,只定义了赋值运算符,没有算术运算符

band = orange;	//正确
++band;	//错误
band = orange + red; 	//错误

而且,枚举量是整型,可以被提升为int类型,但是int类型不可以转换为枚举类型

int color = blue;	//blue是枚举常量,color是int变量
band = 3;	//3是int类型,band是枚举类型,不能将int转换为枚举
color = 3 + red;	//正确使用,运算前red由枚举类型转换为int类型,再进行运算
band = orange + red;	//错误使用,枚举没有算术运算符,且运算前orange和red转换为int类型了

设置枚举量的值

使用赋值运算符来显式地设置枚举量的值:

enum bits {one = 1, two = 2, four = 4, eight = 8};	//指定的值必须是整数
enum bigstep {first, second = 100, third};	//first默认为0,没被初识化的枚举量的值比前面的枚举量大1,故third值为101
enum spectrum {zero, null = 0, one, numero_uno = 1};	//这属于正确设置枚举量的值

枚举的取值范围

enum bigstep {first, second = 100, third};

枚举取值范围的定义:上限,找出枚举量的最大值,然后找到大于这个最大值的、最小的2的幂,将他减1,例如:bigstep中最大枚举值是101,在2的幂中,比这个数大的最小值为128,因此取值范围为127;下限,找出枚举量的最小值,如果它不小于0,则取值范围下限为0,否则,采用与寻找上限一样的方式,弹药加上负号,例如:如果最小值是-6,而比它小的、最大的2的幂是-8,因此下限为-7。

enum bits {one = 1, two = 2, four = 4, eight = 8};
bits myflag;
myflag = bits(6);	//属于正确的赋值,虽然6不是枚举值,但6在枚举的取值范围之内,可以使用
posted @ 2020-07-23 16:07  Budcs  阅读(115)  评论(0)    收藏  举报