C++ 第四章 4.1
一、数组
1、 定义: 数组(array)是一个数据格式,能够存储多个同类型的值。(12个short值(这些值表示每 个月的天数)或365个float值(这些值指出一年每天在食物方面的开销))。
2、要创建数组,可使用声明语句。
3、 数组声明应指出以下三点:
1、存储在每个元素中的类型;
2、数组名;
3、数组中的元素数。
4、在 C++中,可以通过修改简单变量的声明,添加中括号(其中包含元素数目)来完成数组声明。
例如:声明一个名为 months 的数组,该数组有12个元素,每个元素都可以存储一个 short 类型的值: short months[12] //creates array of 12 short
事实上,可以将数组中的每个元素看作是一个简单变量。
5、声明数组的通用格式如下:typeName arrayName[arraySize]; 表达式arraySize指定元素数目,它必须是整型数(如 10)或 const值,也可以是常量表达式(如8*sizeof(int)),即其中所有的值在编译时都是已知的。
6、数组之所以被称为复合类型,是因为它是使用其他类型来创建的(C语言使用术语 “ 派生类型”,但由于C++对类关系使用术语 “派生”,所以它必须创建一个新术语)。
7、程序清单4.1中的马铃薯分析程序说明了数组的一些属性,包括声明数组,给数组元素赋值以及初始化数组。如下:
//arrayone.cpp -- small arrays of inegers #include <iostream> /*主函数*/ int main() { using namespace std; int yams[3]; //creates array with threa elements (定义一个数组) yams[0] = 7; //assign value to first element (给yams[]数组赋值) yams[1] = 8; yams[2] = 6; int yamcosts[3] = { 20,30,5 }; //create, initialize array //NOTE: It your C++ compiler or translator can't initialeze //this array ,use static int yamcosts[3] instead of //int yamcosts[3] /*打印出来或是显示出来*/ cout << "Total yams = "; cout << yams[0] + yams[1] + yams[2] << endl; cout << "The package with " << yams[1] << " yams costs "; cout << yamcosts[1] << " cents per yam.\n"; int total = yams[0] * yamcosts[0] + yams[1] * yamcosts[1]; //定义一个 total ,并赋值 total = total + yams[2] * yamcosts[2]; cout << "The total yam expense is " << total << " cents.\n"; cout << "\nSize of yams array = " << sizeof yams; //sizeof yams : 这表明yams是一个数组,而yams[1]只是一个int变量 cout << "bytes.\n"; cout << "Size of one element = " << sizeof yams[0]; return 0; }
该程序的输出结果 :
8、C++有几条关于初始化数组的规则,它们限制了初始化的时刻,决定了数组的元素数目与初始化器中值的数目不相同时将发生的情况。
初始化数组时,提供的值可以少于数组的元素数目。例如:float hotelTips[5] = {5.0 , 2.5}; 如果只对数组的一部分进行初始化,则编译器将把其他元素设置为 0 。
9、C++11 数组初始化方法
数组以前就可使用列表初始化,但 C++11 中的列表初始化新增了一些功能。
首先,初始化数组时,可省略等号 (=):
double earnings[4] {1.2e4, 1.6e4, 1.1e4, 1.7e4}; //okay with C++11
其次,可不在大括号内包含任何东西,这将把所有元素都没有都设置为零:
unsigned int counts[10] = {}; //all elements set to 0 float balances[100] { }; //all elements set to 0
C++标准模板库(STL)提供了一种数组替代品---------模板类vector,而C++11新增了模板类array。
二、字符串
1、 定义:字符串是存储在内存的连续字节中的一系列字符。C++处理字符串的方式有两种。第一种来自C语言,常被称为C-风格字符串(C-style string)。另一种基于string类库的方法。
存储在连续字节中的一系列字符意味着可以将字符串存储在 char 数组中,其中每个字符都位于自已的数组元素中。
在数组中使用字符串:
要将字符串存储到数组中,最常用的方法有两种-————将数组初始化为字符串常量、将键盘或文件输入读入到数组中。
程序清单如下演示这两种方法:
//stringe.cpp -- storing strings in an array #include <iostream> #include <cstring> //for the strlen() function /*主函数*/ int main() { using namespace std; const int Size = 15; char name1[Size]; //empty array char name2[Size] = "C++owboy";//initalized array //NOTE: some implementations may require the staic keyword //to initialize the array name2 cout << "Howdy! I'm " << name2; cout << "! What's your name?\n "; cin >> name1; cout << "well, " << name1 << ", your name has "; cout << strlen(name1) << " letters and is stored\n"; cout << "in an array of " << sizeof(name1) << " bytes.\n"; cout << "Your initial is " << name1[0] << ".\n"; name2[3] = '\n'; //set to null character cout << "Here are the firet 3 characters of my name: "; cout << name2 << endl; return 0; }
结果显示:
2、字符串输入
上面程序strings.cpp有一个缺陷。下面的程序将揭开它的面纱,揭示了字符串输入的技巧。
//instrl.cpp -- reading more than one string #include <iostream> /*主函数*/ int main() { using namespace std; const int ArSize = 20; char name[ArSize]; char dessert[ArSize]; cout << "Enter your name: \n"; cin >> name; cout << "Enter your favorite dessert : \n"; cin >> dessert; cout << "I have some delicous " << dessert; cout << " for you, " << name << ".\n"; return 0; }
结果显示如下:
该程序的意图很简单:读取来自键盘的用户和用户喜欢的甜点,然后显示这些信息。
3、每次读取一行字符串输入
每次读取一个单词通常部不是最好的选择。
具体地说,需要采用面向行而不是面向单词的方法。在 istream 中的类(如 cin)提供了一些面向行的类成员函数:getline() 将丢弃换行符,而 get() 将换行符保留在输入序列中,下面详细介绍它 们,首先介绍 getline()。
1)面向行的输入:getline()
getline() 函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。要调用这种方法,可以使用 cin.getline() 。
该函数有两个参数:第一个参数是用来存储输入行的数组的名称,第二个参数是要读取的字符数。如果这个参数为 20 ,则函数最多读取 19 个字符,余下的空间用于存储自动在结尾处添加的空字符。getline() 成员函数在读取指定数目的字符或遇到换行符时停止读取。
例如:假设要使用 getline() 将姓名 读入到一个包含 20 个元素的 name 数组中。可以使用这样的函数调用:
cin.getline(name,20);
这将把一行读入到 name 数组中——如果这行包含的字符不超过 19 个。
下面程序使用cin.getlien() ,而不是简单的 cin,除此之外,该程序没有做其他修改。
//instr2.cpp -- reading more than word with retline // 阅读多于与retline的单词 #include <iostream> /*主函数*/ int main() { using namespace std; const int ArSize = 20; char name[ArSize]; char dessert[ArSize]; cout << "Enter your name : \n"; cin.getline(name, ArSize); //reads through newline(读通过换行符) cout << "Enter your favorit dessert : \n"; cin.getline(dessert,ArSize); cout << "I have some delicious " << dessert; cout << " for you, " << name << ".\n"; return 0; }
结果显示如下:
2)面向行的输入:get()
在 istream 类有另一个名为 get() 的成员函数,该函数有几种变体。其中一种变体的工作方式与 getline() 类似,它们接受的参数相同,解释参数的方式也相同,并且都读取到行尾。可 get 并不再读取并丢弃换行符,而是将其留在输入队列中。若我们连续调用两次调用 get():
cin.get(name,ArSize); cin.get(dessert,Arsize); // a problem (一个问题)
由于第一次调用后,换行符将留在输入队列中,因此第二次调用到的第一个字符便是换行符,因此 get() 认为已到达行尾,而没有发现任何可读取的内容。
在 get() 有另一种变体。使用不带任何参数的 cin.get() 调用可读取下一个字符(即使是换行符),
因此可以用它来处理换行符,为读取下一行输入做好准备,也就是说,可以采用下面的调用序列:
cin.get(name,ArSize); //read first line (读第一行) cin.get(); //read newline (读换行符) cin.get(dessert,Arsize); //read second line (读第二行)
另一种使用 get() 的方式是将两个类成员函数并接起来(合并),如下所示:
cin.get(name,ArSize).get(); //concatenate member functions (将成员函数)
之所以可以这样做,是由于 cin.get(name,ArSize) 返回一个 cin 对象,该对象随后将被用来调用 get()函数。同样,下面的语句将把输入中连续的两行分别读入到数组 name1 和 name2 中,其效果与两次调用 cin.getline() 相同:
cin.getline(name1,ArSize),getline(name2,ArSize);
下面程序采用并接方式:
//instr3.cpp -- reading more than one word with get() & get() #include <iostream> /*主函数*/ int main() { using namespace std; const int ArSize = 20; char name[ArSize]; char dessert[ArSize]; /*显示或打印*/ cout << "Enter youy name:\n"; cin.get(name,ArSize).get(); //read string,newline (读取字符串,换行符) cout << "Enter your favorite dessert:\n"; cin.get(dessert,ArSize).get(); //读取字符串,换行符 cout << "I have some delicious " << dessert; cout << " for you ," << name << ".\n"; return 0; }
结果显示:
需要指出的一点是,C++ 允许函数有多个版本,条件是这些版本的参数列表不同。如果使用的是 cin.get( name,ArSize),则编译器知道是要将一个字符串放入数组中,因而将使用适当的成员函数。若使用是 cin.get ( ),则编译器知道是读取一个字符。在第 8 章将探索这种特性——函数重载。
3)空行和其他问题
当 getline( ) 或 get( ) 读取空行时,将发生什么情况?最初的做法是,下一条输入语句将在前一条 getline( ) 或 get( )结束读取的位置开始读取;但当前的做法是,当 get( )(不是 getline( ))读取空行后将设置失效位(failbit)。这意味着接下来的输入将被阻断,但可以用下面的命令来恢复输入:
cin.clear();
另一个潜在的问题是,输入字符串可能比分配的空间长。如果输入行包含的字符数比指定的多,则getline( ) 和 get( ) 将把余下的字符留在输入队列中,而 getline( )还会设置失效位,并关闭后面的输入。
4、混合输入字符串和数字
混合输入数字和面向行的字符串会导致问题。如下程序:
//unmstr.cpp -- following number input with line input #include <iostream> /*主函数*/ int main() { using namespace std; cout << "What year was your house built?\n"; int year; //定义一个int类型变量 cin >> year; //输入一个数字 cout << "What is its street address?\n"; char address[80]; //定义一个char数组 cin.getline(address, 80); cout << "Year built: " << year << endl; cout << "Address: " << address << endl; cout << "Done!\n"; return 0; }
结果显示:
三、string 类简介
ISO / ANSI C++98 标准通过添加 string 类扩展了 C++ 库,因此现在可以 string 类型的变量(使用 C++ 的话说是对象)而不是字符数组来存储字符串。
要使用 string 类,必须在程序中包含头文件 string。string 类位于名称空间 std 中,因此必须提供一条 using 编译指令,或者使用 std::string 来引用它。
string 类定义隐藏了字符串的数组性质,让你能够像处理普遍变量那样处理字符串。如程序strtypel.cpp:
//strtype1.cpp -- using the C++ string class :使用c++ string类 #include <iostream> #include <string> //make string class available :提供字符串类 /*主函数*/ int main() { using namespace std; char charr1[20]; //create an empty array :创建一个空数组 char charr2[20] = "jaguar"; //create an initialized array :创建一个数组初始化 string str1; //create an empty string object :创建一个空字符串对象 string str2 = "panther"; //create an initialized string :创建一个初始化字符串 cout << "Enter a kind of feline : "; cin >> charr1; //请在键盘输入 cout << "Enter another kind of feline : "; cin >> str1; //请在键盘输入( use cin for input:使用cin输入) cout << "Eere are some felines : \n"; cout << charr1 << " " << charr2 << " " << str1 << " " << str2 //use cout for output :使用cout为输出 << endl; cout << "The third letter in " << charr2 << " is" << charr2[2] << endl; cout << "The third letter in " << str2 << " is " << str2[2] << endl; //use array notation :使用数组表示法 return 0; }
这个示例可知,在很多方面,使用 string 对象的方式与使用字符数组相同。
1)可以使用C-风格字符串来初始化 string 对象。
2)可以使用 cin 来将键盘输入存储到 string 对象中。
3)可以使用 cout 来显示 string 对象。
4)可以使用数组表示法来访问存储在 string 对象中的字符。
上例表明,string 对象和字符数组之间的主要区别是:可以将 string 对象声明为简单变量,而不是数组:
string str1; //create an empty string object :创建一个空字符串对象 string str2 = "panther" //create an initialized string :创建一个初始化字符串
类设计程序能够自动处理 string 的大小。
例如:str1 的声明创建一个长度为 0 的 string 对象,但程序将输入读取到 str1 中时 ,将自动调整 str1 的长度:
cin>>str1; //str1 resized to fit input :str1调整大小以适应输入
使用 string 对象更方便,也更安全,从理论上说,可以将 char 数组视为 一组用于存储一个字符串的 char存储单元,而 string 类变量是一个表示字符串的实体。
1、C++11 字符串初始化
C++11 也允许将列表初始化用于C-风格字符串和 string 对象:
char first_date[] = {"Le Chapon Dodu"}; char second_date[] = {"The Elegant Plate"}; string third_date[] = {"The Bread Bowl"}; string fourth_date = {"Hank's Pine Eats"};
2、赋值、拼接和附加
使用 string 类时,某些操作比使用数组时更简单。
例如:不能将一个数组赋给另一个数组,但可以将一个 string 对象赋给另一个 string 对象:
char charr1[20]; //create an empty array :创建一个空数组 char charr2[20] = "jaguar"; //create an initialized array :创建一个数组初始化 string str1; //create an empty string object :创建一个空字符串对象 string str2 = "pantner"; //create an initialized string :创建一个初始化字符串 charr1 = charr2; //INVALID,no array assignment :无效的,没有数组赋值 str1 = str2; //VALID object assignment ok :有效的对象分配好
string 类简化了字符串合并操作。可以使用运算符 + 将两个 string 对象合并起来,还可以使用运算符 += 将字符串附加到 string 对象的末尾。
也可以这样写:
string str3; //创建一个空字符串对象 str3 = str1 + str2; //assign str3 the jpined strings :分配jpined字符串 str1 += str2; //add str2 to the end of str1 :将str2添加到str1的末尾
下面程序使用上面这些用法。可以将 C-风格字符串或 string 对象与 string 对象相加,或将它们附加到 string 对象的末尾。
//strtye2.cpp -- assigning,adding,and appending #include <iostream> #include <string> //make string class available 提供字符串类(string 的头文件) /*主函数*/ int main() { using namespace std; string s1 = "penguin"; //创建一个初始化字符串 string s2, s3; //创建两个空字符串对象 cout << "You can assign one string object to another : s2 = s1\n"; s2 = s1; cout << "s1: " << s1 << " ,s2: " << s2 << endl; cout << "You can assign a C-style string to a string object.\n"; cout << "s2 = \"buzzard\"\n"; s2 = "buzzard"; cout << "s2: " << s2 << endl; cout << "You can concatenate strings: s3 = s1 + s2\n"; s3 = s1 + s2; cout << "s3: " << s3 << endl; cout << "You can append strings.\n"; s1 += s2; cout << "s1 += s2 yields s1 = " << s1 << endl; s2 += "for a day"; cout << "s2 += \"for a day\" yields s2 = " << s2 << endl; return 0; }
结果显示:
3、string 类的其他操作
在 C++ 新增 string 类之前,程序员也需要完成诸如给字符串赋值等工作。对于 C -风格字符串,程序员使用 C 语言库中的函数来完成这些任务。头文件 cstring (以前为 string.h)提供了这些函数。
例如:可以使用函数 strcpy( )将字符串复制到字符数组中,使用函数 strcat( )将字符串附加到字符数组末尾:
strcpy[charr1,charr2]; //copt charr2 to charr1:科普特人charr2,charr1
strcat[charr1,charr2]; //append contents of charr2 to charr1
//append内容的charr2到charr1
下面的程序对用于 string 对象的技术和用于字符数组的技术进行了比较:
//strtype3.cpp -- more string class featuree #include <iostream> #include <string> //make string class available:提供字符串类 #include <cstring> //C-style string library:c风格的字符串库 /*主函数*/ int main() { using namespace std; char charr1[20]; char charr2[20] = "jaguar"; string str1; string str2 = "panther"; //assignment for string objects and character arrays:分配字符串对象和字符数组 str1 = str2; //copy str2 to str1 :复制str2 str1 strcpy(charr1, charr2); //copy charr2 to charr1 :复制charr2 charr1 //appending for string objects and character arrays:附加字符串对象和字符数组 str1 += " paste"; //add paste to end of atr1:添加粘贴到atr1结束 strcat(charr1, " juice"); //add juice to end of charr1:将果汁添加到charr1末端 //finding the Jength of a string object and a C-style string //找到一个字符串对象的Jength和c样式的字符串 int len1 = str1.size(); //obtain length of str1:获得str1长度 int len2 = strlen(charr1); //obtain length of charr1:获得charr1长度 cout << "The string " << str1 << " contains " << len1 << " characters.\n"; cout << "The string " << charr1 << " contains " << len2 << " characters.\n"; return 0; }
结果显示:
处理 string 对象的语法通常比使用 C 字符串函数简单,尤其是执行较为复杂的操作时。
例如:对于下述操作:
str3 = str1 + str2
使用 C-风格字符串时,需要使用的函数如下:
strcpy(charr3,charr1);
strcat(charr3,charr2);
另外,使用字符数组时,总是存在目标数组过小,无法存储指定信息的危险,如下:
char site[10] = "house"; strcat(site,"of pancakes"); //memory problem:内存问题
函数 strcat () 试图将全部 12个字符复制到数组 site 中,这将覆盖相邻的内存。这可能导致程序终止,或者程序继续运行,但数据被损坏。string 类具有自动调整大小的功能,从而能够避免这种问题发生。
C 函数数据确实提供了与 strcat( ) 和 strcpy( ) 类似的函数——strncat( ) 和 strncpy( ), 它们接受指出目标数组最大允许长度的第三个参数,因此更为安全,但使用它们进一步增加了编写程序的复杂度。
下面是两种确定字符串中字符数的方法:
int len1 = str1.size(); //obtain length of str1:获得str1长度 int len2 = strlen(charr1); //obtain length of charr1 :获得charr1长度
函数 strlen( ) 是一个常规函数,它接受一个C- 风格字符串作为参数,并返回该字符串包含的字符数。函数 size( ) 的功能基本上与此相同,这种句法表明,str1 是一个string 对象,而 size() 是 string 类的一个方法。总之,C 函数使用参数来指出要使用哪个字符串,而 C++ string 类对象使用对象名和句点运算符来指出要使用哪个字符串。
4、string 类 I / O
可以使用 cin 和运算符 << 来将输入存储到 string 对象中,使用 cout 和运算符 <<来显示 string 对象,其句法与处理 C- 风格字符串相同。但每次读取一行而不是一个单词时,使用的句法不同,如下程序表明这一点:
//strtype4.cpp -- line input #include <iostream> #include <string> //make string class available :提供字符串类 #include <cstring> //C-style string library:c风格的字符串库 /*主函数*/ int main() { using namespace std; char charr[20]; string str; cout << "Length of string in charr before input: " << strlen(charr) << endl; cout << "Length of string in str before input : " << str.size() << endl; cout << "Enter a line of text : \n"; cin.getline(charr, 20); //indicate maximum length:表示最大长度 cout << " You entered : " << charr << endl; cout << "Enter another line of text : \n"; getline(cin, str); //cin now an argument;no length specifier:现在是一个论点,没有长度说明 cout << "You entered : " << str << endl; cout << "Length of string in charr after input : " << strlen(charr) << endl; cout << "Length of string in str after input : " << str.size() << endl; return 0; }
结果显示:
在用户输入之前,该程序指出数组 char 中的字符串长度为 27,这比该数组的长度要大。这里要两点需要说明。首先,为初始化的数组的内容是未定义的;其次,函数 strlcn( ) 从数组的第一个元素开始计算字节数,直到遇到空字符。在这个例子中,在数组末尾的几个字节后才遇到空字符。
下面是将一行输入读取到数组中的代码:
cin.getline(charr, 20);
这种句点表示法表明,函数 getline( ) 是 istream 类的一个类方法(还记得吗、cin 是一个 istream 对象)。正如前面指出的,第一个参数是目标数组;第二个参数数组长度, getline( ) 使用它来避免超越数组的边界。
下面是将一行输入读取到 string 对象中的代码:
getline(cin, str);
这里没有使用句点表示法,这表明这个 getline( ) 不是类方法。它将 cin 作为参数,指出到哪里去查找输入。另外,也没有指出字符串长度的参数,因为 string 对象将根据字符串的长度自动调整自已的大小。
在引入 string 类之前很久,C++ 就有 istream 类,因此 istream 的设计考虑到了诸如 double 和 int 等基本 C ++数据类型,但没有考虑 string 类型,所以 istream 类中,有处理 double 、int 和其他基本类型的类方法,但没有处理 string 对象的类方法。
由于 istream 类中没有处理 string 对象的类方法,因此您可能会问,下述代码为何可行呢?
cin >>str; //read a word into the atr string object:
//读取一个单词到atr字符串对象
像下面这样的代码使用 istream 类的一个成员函数:
cin >> x; //read a value into a basic C++ type //将一个值读入一个基本的c++类型
前面处理 string 对象的代码使用 string 类的一个友元函数。
四、结构简介
结构是一种不数组更灵活的数据格式,因为同一个结构可以存储多种类型的数据,这使得能够将有关篮球运动员的信息放在一个结构中,从而将数据的表示合并到一起。
结构是用户定义的类型,而结构声明定义了这种类型的数据属性。定义了类型后,便可以创建这种类型的变量。因此创建结构包括两步:首先,定义结构描述——它描述并标记了能够存储在结构中的各种数据类型。然后,按描述创建结构变量(结构数据对象)。
例如:假设 Bloataire 公司要创建一种类型来描述其生产线上充气产品的成员,具体地说,这种类型应存储产品名称、容量(单位为立方英寸)和售价。如下面的结构描述能够满足这些要求:
struct inflatable //structure declaration :结构声明 { char name[20]; //创建一个空字符数组 float volume; double price; }
关键字 struct 表明,这些代码定义的是一个结构的布局。标识符 inflatable 是这种数据格式的名称,因此新类型的名称为 inflatable 。这样,便可以像创建 char 或 int 类型的变量那样创建 inflatable 类型的变量了。
接下来的大括号中包含的是结构存储的数据类型的列表,其中每个列表项都是一条声明语句。这个例子使用了一个适合用于存储字符串的 char 数组、一个 float 和一个 double 。列表中的每一项都被称为结构成员,因此 infatable 结构有 3 个成员(如下图) ,总之,结构定义指出了新类型(这里是 inflatable)的特征。
定义结构后,使可以创建这种类型的变量了:
inflatable hat; //hat is a structure variable of type inflatable : 帽子是可充气的结构变量 inflatable woopie_cushion; //type inflatable variable inflatable mainframe; //type inflatable variable : 充气型变量
如果您熟悉 C 语言中的结构,则可能已经注意到了,C ++ 允许在声明结构变量时省略关键字 struct :
struct inflatable goose; //keyword struct required in C :关键字结构要求在C inflatable vincent; //keyword struct not required in C++:在c++中不需要使用关键字结构
在 C++ 中,结构标记的用法与基本类型名相同。这种变化强调的是,结构声明定义了一种新类型。在 C++ 中,省略 struct 不会出错。
1、在程序中使用结构
介绍结构的主要特征后,下面在一个使用结构的程序中使用这些概念。下面的程序说明有关结构的这些问题,还演示了如何初始化结构:
//structur.cpp -- a simple structure :一个简单的结构 #include <iostream> /*创建结构*/ struct inflatable //结构声明 (structure declaration) { char name[20]; //创建一个空字符数组 float volume; //创建一个float类型的(volume)体积 double price; //创建一个double类型的(price)价格 }; /*主函数*/ int main() { using namespace std; inflatable guest = { "Gloious Gloria", //name value 名义价值 1.88, //volume value 体积值 29.99 //price value 价格的价值 }; //guest is a structure variable of type inflatable 客人是一种可充气的结构变量 //It's initialized to the indicated values :它被初始化为表示值 inflatable pal = { "Audacious Arthur", 3.12, 32.99 }; //pal is a second variable of type inflatable :pal是可充气的第二个变量 //NOTE:some implementations require using :注意:一些实现需要usi //static inflatable guest = : 静态充气客人= cout << "Expand your guest list with " << guest.name; cout << " and " << pal.name << "!\n"; //pal.name is the name member of the pal variable cout << "You can have both for $"; cout << guest.price + pal.price << ".\n"; return 0; }
结果显示: