* 复合类型(类除外)
* new和delete以及如何使用它们来管理数据
* string类(另外一种处理字符串的途径)ios
声明数组的通用格式是:git
arrayType arrayName[arraySize]
arraySize必须是可知数据,可使用 new 运算符来避开这种限制;程序员
【注:】express
一、编译器不会检查使用的下标是否有效。若对无效的下标进行赋值,编译器不会指出错误。
但程序运行后,赋值可能引起问题,它可能破坏数据或代码,也可能致使程序异常终止。so,必须确保只使用有效的下标值。编程
二、数组
sizeof是返回类型或者数据对象的长度(单位:字节);
若是sizeof用于数组名,获得的是整个数组的字节数;
若是用于数组元素,获得是元素的长度(单位:字节);函数
数组初始化的规则:工具
1. 只有在定义数组时能够初始化;然而,可使用下标的方式来进行赋值;
2. 若是没有对定义的数组进行初始化,则元素的值是不肯定的,意味着元素的值为之前驻留在该内存单元中的值;
3. 初始化时,若仅对一部分元素进行赋值,则其余的元素值为0;
4. 所以将数组中全部元素的值赋值为0,仅须要arrayName[500] = {0};
5. 若是初始化数组时,[]内的值为空,那么编译器将计算元素个数;测试
4.2 字符串编码
C++处理字符串的两种方式:
* C-风格字符串;
* string类;
1、C-风格字符串:
C-风格字符串有一个特殊的规则:以空字符串结尾,空字符串被写做\0,其ASCII码值为0,用来标记字符串的结尾。
char dog[8] = {'b', 'e', 'a', 'u', 'x', '', 'I', 'i'};
char cat[8] = {'f', 'a', 't', 'e', 's', 's', 'a', '\0'};//只有这个是字符串
C++有不少字符串处理函数,好比cout,它们逐个处理字符串中的字符,直至遇到空字符;
cout显示cat将显示7个字符,遇到空字符串结束;
cout显示dog显示8个字符,并接着讲内存中随后各个字节解释为要打印的字符,直至遇到空字符结束;
so,不该将不是字符串的char数组看成字符串来处理。
一种更好的方法,将字符数组初始化为字符串-这种字符串称为字符串常量或字符串字面值;
char birds[11] = "Mr. Cheeps"; char fish[] = "Bubbles";
这种初始化字符串的方式将隐式包含空字符串。
另外,各类C++输入工具经过键盘输入,将字符串都入到char数组时,将自动加上空字符做为结尾;
固然,应当确保数组足够大,可以存储字符串的全部字符-包括空字符;
让数组比字符串长没有什么害处, 只是会浪费一些空间,由于字符串的函数根据空字符串的位置[遇到空字符则中止处理],而不是数组长度进行处理。
注:不能将'S' 和 "S"互换:
'S'是字符常量,是83的另外一种写法,是字符串编码的简写表示。
"S"是字符串常量,表明字符S和\0组成的字符串,另外,“S”实际上表示的是字符串所在的内存地址。
任何两个由空白(空格、制表符和换行符)分隔的字符串常量将自动拼接成一个。【拼接时不会在被链接的字符串之间添加空格】
将字符串存储在数组中,两种方法:
* 将数组初始化为字符串常量
* 将键盘或者文件都入到数组中,经过cin
char name1[15] = "Basicman" ; char name2[15] = "C++owboy"; strlen(name1); sizeof(name1);
sizeof指出整个数组的长度:15个字符;strlen返回存储在数组中的字符串的长度:8(strlen只计算可见的字符,而不把空字符计算在内),但数组长度须要strlen()+1;
另外,对于name2,若执行该操做,name2[3]='\0';这使得该字符串在第3个字符的时候即结束,虽然数组中还有其余的字符。
#include<iostream> int main() { using namespace std; const int ArySize = 20; char name[ArySize]; char dessert[ArySize]; cout << "Enter your name:\n"; cin >> name; cout << "Enter your favorite dessert:\n"; cin >> dessert; cout << "I have some desicious dessert " << dessert ; cout << " for you " << name << endl; return 0; }
具体输出状况是:
Enter your name: Jason Yang Enter your favorite dessert: I have some desicious dessert Yang for you Jason
因为不能经过键盘输入空字符串\0,所以cin使用空白(空格、换行符或制表符)来肯定字符串的结束位置。这意味着cin在获取字符数组输入时只读取一个单词。读取该单词以后,cin将该单词放入数组中,并自动添加空字符。这样实际结果是,cin吧Jason做为第一个字符,并将它放到name中,把Yang放到输入队列中。当cin在输入队列中搜索甜点时,发现了Yang,所以读取Yang,将Yang放入到数组dessert中。
另外一个问题是,输入字符串的长度可能大于目标数组长度,那么,该怎么办呢?这须要使用cin的更高级特性。在17章进行介绍。
面向行的输入:
* cin类的getline
* cin类的get
getline丢弃换行符,get将换行符放入到输入队列中;
一、面向行的输入:getline()
经过回车输入的换行符来肯定字符串的结束;
* 有两个参数,第一个参数表示要输入的数组名称,第二个参数代码要输入的字符串的长度;
* 若是参数为20,则该函数仅接受19个字符,剩余的一个字符用来存储在结尾处自动添加的空字符;
* getline在读取指定数目的字符或者遇到换行符的时候中止;
二、面向行的输入:get()
使用方式与getline相同,不一样点:get读取换行符并保留在输入队列中。
cin.get(name, ArSize); cin.get(dessert, ArSize);
第一次调用,将换行符留在输入队列中,所以第二次调用看的到第一个字符就是换行符,且认为已经达到行尾。
get有一种变体,cin.get()可读取下一个字符(即便是换行符)。
cin.get(name, ArSize); cin.get(); cin.get(dessert, ArSize);
另外一种使用get的方式是将两个类成员函数链接起来:
cin.get(name, ArSize).get();
由于cin.get(name, ArSize)返回一个cin对象;
下面的语句将输入的两行语句分别读入到数组name和dessert中。
cin.getline(name, ArSize).getline(dessert, ArSize);
三、为何使用get而不是getline呢?
* 由于老式实现没有getline;
* 怎么肯定中止读取的缘由是由于换行符或者是数组已经满了呢?查看下一个输入符,若是是换行符,说明已经读取了整行;若是不是换行符说明数组已经满了,该行还有其余的输入。
#include<iostream> int main() { using namespace std; cout << "Enter the year:" << endl; int year; cin >> year; cout << "Enter the address: \n"; char address[80]; cin.getline(address, 80); cout << "year: " << year << endl; cout << "address: " << address << endl; return 0; }
运行结果是:
Enter the year: 2000 Enter the address: year: 2000 address:
用户根本没有输入地址的机会,由于cin读取年份时,将回车键生成的换行符放在了输入队列中。cin.getline看到换行符后,认为是一个空行,将空字符串赋值给address。
解决方法:丢弃换行符。
方法:
* 没有任何参数的cin.get()
* 使用一个char参数的cin.get(ch);
C++ 一般使用指针而不是数组来处理字符串。咱们再介绍指针以后,在介绍字符串方面的特性。
下面介绍一种新的字符串处理方式:string类。
要使用string类,必须包含头文件string,string类位于命名空间std中。须要添加using编译指令;
string类与使用数组字符串有不少类似之处:
* 可使用C-字符串风格来初始化string对象
* 可使用cin将键盘输入存储到string对象
* 可使用cout来显示string对象
* 可使用数组表示法来访问存储在string对象中的字符
string对象与数组字符串的主要区别就是:
能够将string声明为简单变量;
类设计让程序自动处理string的大小;
能够将一个string对象的值赋值给另外一个string对象;可使用+将两个字符串链接起来,可使用+=将字符串附加到string对象的末尾;
在有string类以前,对于C-风格字符串,使用C语言库中的函数来完成,头文件ctring提供了这些函数。
eg:
* strcpy()将字符串赋值到字符数组
* strcat()将字符串附加到字符数组中
不一样之处:
赋值:
* string,赋值操做:str2 = str1;
* C-风格字符换,赋值操做:strcpy(ch2, ch1);
附加:
* string,附加操做:str2 += " Boy";
* C-风格字符串,附加操做:strcat(ch2, " Girl");
另外,使用字符数组,老是存在目标数组长度太小,没法存储指定信息的危险。
函数strcat若是试图将所有12个字符复制到数组site中,这将覆盖相邻的内存。这可能致使程序种植,或者程序继续运行,但数据被损坏。
string具备自动调节大小的功能。
获取长度:
* strlen(ch2)
* str2.size()
* 使用cin >> 将输入存储到string对象中
* 使用cout << 显示string对象
其句法与C-风格字符串类似。但每次读取一行而不是一个单词时,使用的句法不一样。
* cin.getline(charr, 20);
* getline(cin, strr);
代码以下:
#include<iostream> #include<string> #include<cstring> int main() { using namespace std; char charr[20]; string strr; cout << "the length of charr is: " << strlen(charr) << endl; cout << "the length of strr is: " << strr.size() << endl; cout << "Enter the charr: " << endl; cin.getline(charr, 20); cout << "Enter the strr: " << endl; getline(cin, strr); cout << "the length of charr is: " << strlen(charr) << endl; cout << "the length of strr is: " << strr.size() << endl; return 0; }
运行结果以下:
1. 未初始化的数组内容是未定义的;其次,strlen从数组的第一个元素开始计算字节数,直至遇到空字符;
2. 未初始化的string对象长度自动被设置为0;
3. C-风格字符串getline用法,cin.getline(charr, 20);
4. string getline用法,getline(cin, strr); 代表getline不是类方法,将cin做为参数,指出要到哪里查找输入,未指定长度,string对象会自动调整大小;
问题:为什么一个getline是类方法,一个不是呢?
结构:用户定义类型,能够存储多种类型的数据。
使用:
1. 定义结构描述--描述标记可以存储在结构中的各类数据类型
2. 按照描述建立结构变量
struct student { int age; string name; };
* 定义结构以后,就能够建立这种类型的变量了;
* C++ 容许在声明结构变量时省略struct关键字,结构标记的用法和基本类型的用法相同;
* 可使用成员访问符(.)来访问成员;
eg:声明和初始化(逗号分割,并用花括号括起)
student stu1 = { 24, "cql" };
定义结构类型的位置很重要:
* 定义在main函数中,内部声明仅能够被该声明所属的函数使用;
* 放到main的外面,位于函数外面的称为外部变量,外部声明能够被后面的全部函数访问;
C++ 也容许定义外部变量,但不提倡声明外部变量,提倡外部声明结构,另外,外部声明符号常量一般更合理。
C++ 结构体容许将string做为成员吗?能够,只要能够编译经过。
* 能够将结构做为参数进行传递,以及让函数返回一个结构;
* 使用赋值运算符(=)将结构赋值给另外一个结构,即便结构中包含数组;这种赋值被称为成员赋值;
* 能够同时完成定义结构和声明结构变量
struct student { int age; string name; } stu1, stu2;
甚至能够以这种方式既声明又初始化:
struct student { int age; string name; } stu1 = { 24, "cql" };
另外,C++的结构不只仅能够有成员变量以外,还能够有成员函数。这点将在类中介绍
建立元素为结构的数组;
struct student { int age; string name; } stu1 = { 24, "cql" };
初始化结构数组的规则能够结合初始化数组和初始化结构的规则。
因为数组中的每一个元素都是结构,所以可使用初始化结构的方式来提供值。最终结果为一个逗号分割,花括号括起来的列表。
一种数据格式,能够存储多个不一样的数据类型,但同时只能存储其中的一种。
union one4all { int int_val; double double_val; string string_val; };
one4all能够存储int、double、string类型,但条件是在不一样的时间;
one4all onetest; onetest.int_val = 10; cout << onetest.int_val << endl; onetest.double_val = 13.8; (int_val has lost) cout << onetest.double_val << endl;
因为共用体能够每次只可能存储一个值,所以它必须有足够的空间来存储最大的成员。因此,共用体的最大程度为其最大成员的长度。
。。。。。。
计算机在存储数据时必须跟踪的3种基本属性;
* 信息存储在何处;
* 存储的值是多少;
* 存储的信息类型;
实现上述策略的方法:
* 定义一个简单变量
* 使用指针
* 指针是一种变量,存储的是值的地址,而不是值自己;
* 获取常规变量的地址方法:使用地址运算符&
* 使用常规变量时,值是指定的量,而地址是派生量
* 使用指针时,地址是指定的量,而值是派生量
* *运算符,将其应用于指针,能够获得该地址处存储的值
指针策略(C++ 内存管理编程理念的核心):
int updates = 6; int * p_updates; p_updates = &updates; cout << "the value of updates is: " << updates << endl; cout << ", *p_updates is: " << *p_updates << endl; cout << "the address of updates is: " << &updates << endl; cout << ", p_updates is: " << p_updates << endl; *p_updates = *p_updates + 1; cout << "the value of updates is: " << updates << endl;
其实,p_updates和updates只不过是硬币的正反面:
* p_updates表示地址,使用*可得到值;
* updates表示值,使用&可得到地址;
因为p_updates指向updates,所以,*p_updates和updates是等价的,能够像使用int变量那样使用*p_updates,甚至能够赋值给*p_updates,这样将修改指向的值,即updates;
计算机须要跟踪指针指向的值的类型,例如,char与double的地址看上去没有什么区别,char和double使用的字节数不一样,内部表示格式也不一样,所以,在声明指针时,须要指明指针指向的数据的类型。
* int*是一种复合类型,是指向int的指针
声明方式以下均可以:
* int *ptr * int* ptr * int*prt * int * ptr
使用一样的语法来声明其余类型的指针变量:
char * char_ptr; double * dou_prt;
虽然char_ptr和 dou_prt指向两种长度不一样的数据类型,可是这两个变量自己的长度一般是相同的。
地址须要2个字节仍是4个字节(取决于具体的计算机系统)(有些系统可能须要更大的地址,系统能够针对不一样类型使用不一样长度的地址)。
C++在建立指针时,将分配用来存储地址的内存,但不会分配用来存储地址指向的值得内存。
int * fellow;
*fellow = 223344;
fellow指向哪里呢?并无将地址赋值给fellow,那么223344将放在哪里呢?
因为fellow没有被初始化,它里面可能有任何值,无论是什么,都将它解释为存储223344的地址。
好比,fellow碰巧值为1200,那么1200存储的值就是223344,即便恰巧1200存储的是代码的地址。
so,警告:必定要在指针使用*运算符以前,将指针初始化为一个肯定的、适当的地址。
4.7.3 指针的数字
4.7.4 使用new来分配内存
4.7.5 使用delete释放内存
4.7.6 使用new来建立动态数组
。。。。。。。。。。
其中注意,测试表达式的值不必定为true或者false,可使用任意表达式,C++将把结果强制转换成true或者false;
若为0,为false;若非零,为true;
C++程序会在须要整数值的时候将true和false转换为1和0,在须要bool值的地方将0和非零转换成false和true;
一、表达式和语句
(任何值)或者(任何有效的值和运算符的组合)都是表达式;每一个表达式都有值
eg:十、22+2七、x=22;x=22这个表达式的值为22;
x = y = z = 0;(赋值运算符从右到左执行)
int x; cout << "1: " << (x = 1000) << endl; cout << "2: " << (x < 3) <<endl; cout << "3: " << (x > 3) <<endl; cout.setf(ios::boolalpha); cout << "2: " << (x < 3) <<endl; cout << "3: " << (x > 3) <<endl;
输出结果:
一般,cout在现实bool值以前会将他们转换成int,但cout.setf(ios::boolalpha);函数设置了一个标记,
该标记命令cout显示true或者false,而不是1或者0;(固然,前提是运算结果是bool)
从表达式到语句的转换:只须要加上分号便可。
二、非表达式和语句
* 对任何表达式加上分号均可以成为语句
* 但语句去掉分号并不同是表达式
三、修改规则
前缀和后缀对执行速度有细微的差异;
* 前缀是将值加1,返回结果;
* 后缀首先复制一个副本,将值加1,返回副本;
相比而言,前缀效率比后缀效率高;
将*和++同时用于指针;怎么处理,取决于运算符的位置和优先级。
* 前缀递增、前缀递减和*优先级相同;从右到左的方式进行结合;
* 后缀递增、后缀递减的优先级比前缀运算符的优先级高;从左到右的方式进行结合;
int age[10] = {10, 20, 30, 40, 50}; int *p; p = age; cout << *p << endl; *p++;// 将p的地址+1,而后获取值,age[1] cout << *p << endl; ++*p;// 获取age[1]的值,而后+1 cout << *p << endl;
注意:
指针递增和递减遵循指针算数规则。所以,若是p指向数组的第一个元素,++p将修改p,使之指向第二个元素。
-= += *= /= %=
多条语句使用花括号括起来;
* 复合语句有一个有趣的特性,语句块中定义一个新的变量,则当程序执行该语句块中的语句时,该变量才存在。
执行完该语句块,变量将被释放。这代表此变量仅在该语句块中才是有用的。
* 若语句块中声明一个变量,外部声明也有一个重名的变量,该怎么处理呢?
* 在声明位置到内部语句块结束的范围以内,新变量隐藏旧变量
逗号运算符容许将两个表达式放到只容许放一个表达式的地方。
string word; char temp; cin >> word; for (int i = 0, j = (word.size()-1); i < j; i++, j--) { temp = word[i]; word[i] = word[j]; word[j] = temp; } cout << word << endl;
到目前为止,逗号运算符最多见的用途是将两个或者更多的表达式放到一个for循环表达式中。
另外,
* 逗号运算符先计算第一个表达式,在计算第二个表达式
int m, n, p; p = (m = 2, n = 2 * m); cout << p << endl;
* 其次,逗号表达式的值是第二部分的值,如上,值为4;
* 逗号运算符的优先级是最低的;
C++提供了6种关系运算符来对数字进行比较。因为字符用其ASCII码表示,所以也能够进行关系比较。
不能将它们用于C-风格字符串,但能够用于string类对象。
< <= == > >= !=
5.1.13 赋值、比较和可能犯的错误
使用=赋值,使用==比较
5.1.14 C-风格字符串比较
假设要知道字符数组中的字符串是否是mate,word是数组名。
word="mate"
注:数组名是数组的地址,字符串常量"mate"也是其地址。这就是判断两个字符串是否存储在相同地址上
使用strcmp()函数。
接受两个字符串地址做为参数,意味着参数能够是指针、字符串常量或者字符数组名。
若两个字符串相同,返回0;
若第一个字符串按字母排序排在第二个字符串以前,返回负值;
若第一个字符串按字母排序排在第二个字符串以后,返回正值;
实际上,按“系统排列顺序”比按“字母排列顺序”更准确。这意味着字符是根据字符的系统编码来比较的。
C-风格字符串经过空字符来定义,而不是其所在数组的长度。这意味着,即便两个字符串被存储在不一样长度的数组中,也多是相同的。
虽然不能使用关系运算符来比较字符串,但却能够来比较字符,由于字符实际上就是整型。
5.1.15 比较string类字符串
类设计可以使用关系运算符进行比较。
之因此可行,是由于类函数重载了这些运算符。具体在12章介绍。
string类重载运算符!=使用条件:至少有一个操做数为string对象,另外一个操做数能够是string对象或者C-风格字符串。
。。。。
cin对象支持3中不一样模式的单字符输入,其用户接口各不相同
cin忽略空格和换行符
char a; int count = 0; cin >> a; while(a != '#'){ count++; cin >> a; } cout << count << endl;
可以读取包括空格、制表符和换行符;
若是熟悉C语言,可能觉得这个程序存在严重的错误。
cin.get(ch)调用将一个值放到ch边两种,意味着改变该变量的值。
C语言中,要修改变量的值,必须将变量的地址传递给函数。
但cin.get(ch),传递的是ch,而不是&ch。在C语言中,这样的代码无效,但在C++中,这样的代码有效。
只要函数将参数声明为引用便可。第8张介绍。
另外,一般,在C++中传递的参数的工做方式与C语言相同。而后,cin.get(ch)并非这样;
char a; int count = 0; cin.get(a); while(a != '#'){ count++; cin.get(a); } cout << count << endl;
cin.get()的三种使用方法:
cin.get(arrayName, arraySize); cin.get(); cin.get(ch);
函数重载,函数重载容许建立多个同名函数,条件式参数列表不一样;
函数以不一样的方式或针对不一样类型执行相同的任务;
。。。。。。。。。。。
在此,仅介绍下声明城市数组的集中方式;
char 指针
const char * cities[5] = { "Beijing", "Shanghai", "Henan", "Tianjin", "Ganchazhuang" }; cout << cities[0] << endl;
char数组的数组:
const char cities[5][25] = { "Beijing", "Shanghai", "Henan", "Tianjin", "Ganchazhuang" }; cout << cities[0] << endl;
string对象数组:
const string cities[5] = { "Beijing", "Shanghai", "Henan", "Tianjin", "Ganchazhuang" }; cout << cities[0] << endl;
* 条件运算符;
* 逻辑运算符;
* 文件输入\输出;
和循环测试条件同样,if测试条件也被将值转换为bool值,所以0将被转换为false,非零为true。
对多条语句添加大括号;
逻辑OR || 逻辑AND && 逻辑NOT !
char yourchoice; cout << "Enter your choice: " << endl; cin >> yourchoice; if (yourchoice == 'Y' || yourchoice == 'y') cout << "you choose" << endl; else if (yourchoice == 'N' || yourchoice == 'n') cout << "you don't choose'" << endl; else cout << "no choice" << endl;
因为程序只读取一个字符,所以只读取响应的第一个字符。意味着用户能够用NO进行回答,程序只读取N.
然而,若是程序后面再读取输入时,将从O开始读取,O已经进入的输入队列了。
char * ch_ptr = "my god"; cout << ch_ptr << endl;
程序正常运行,但该方式不建议使用,已经被弃用
逻辑OR和逻辑AND运算符优先级 < 关系运算符
!运算符优先级> 全部关系运算符和算术运算符>逻辑OR和逻辑AND
* C++ 可用and 、or 和not;
* C可用and、or和not,前提:包含头文件iso646.h
cctype:与字符相关的、方面的函数软件包;
函数名称返回值isalnum()若是参数是字母数字,即字母或数字,该函数返回trueisalpha()若是参数是字母,该函数返回真isblank()若是参数是空格或水平制表符,该函数返回trueiscntrl()若是参数是控制字符,该函数返回trueisdigit()若是参数是数字(0~9),该函数返回trueisgraph()若是参数是除空格以外的打印字符,该函数返回trueislower()若是参数是小写字母,该函数返回trueisprint()若是参数是打印字符(包括空格),该函数返回trueispunct()若是参数是标点符号,该函数返回trueisspace()若是参数是标准空白字符,如空格、进纸、换行符、回车
、水平制表符或者垂直制表符,该函数返回true
isupper()若是参数是大写字母,该函数返回trueisxdigit()若是参数是十六进制的数字,即0~九、a~f、A~F,该函数返回truetolower()若是参数是大写字符,则返回其小写,不然返回该参数toupper()
若是参数是小写字母,则返回其大写,不然返回该参数
什么是控制字符?
什么是打印字符?
switch(integer-expression) { case label1: statement(s) case label2: statement(s) ... default statement(s) }
integer-expression必须是一个结果为整数值的表达式。
每一个标签都必须是整数常量表达式。
最多见的标签是int或char常量、枚举量。
cin没法识别枚举类型(它不知道程序员是如何定义他们的)。所以要求用户输入一个整数。
当switch语句将int值和枚举量标签进行比较时,将枚举量提高为int。
另外,在while循环测试条件中,也会将枚举量提高为int类型。
enum {red, green}; int code; cin >> code; while(code >= red && code <= black) { switch(code){ case red: cout << "oh,red." << endl; break; case green: cout << "oh,green." << endl; break; } cin >> code; }
switch和if else相比,if else更通用。
switch的每一个case标签值必须是单独的值且必须是整数(包括char)。
假设要编写一个将一系列数字读入到数组中的程序,并容许用户在数组填满以前结束输入。
若是用户输入一个单词,而不是一个数字,状况将如何呢?发生这种类型不匹配的状况时,将发生4中状况:
* n值保持不变;
* 不匹配的输入将被留在输入队列中;
* cin对象中的一个错误标记被设置;
* 对cin方法的调用将返回false(若是被转换为bool类型);
方法返回false意味着能够用非数字输入来结束读取数字的循环。
非数字输入设置错误标记意味着必须重置该标记,程序才能继续读取输入。clear方法重置错误输入标记,同时也重置文件尾(EOF条件)。输入错误和EOF都将致使cin返回false。
while(i < MAX && cin >> fishes[i]){ cout << fishes[i] << endl; i++; } cin >> fishes[i] ;
是一个cin方法函数调用,该方法返回cin。若是cin位于测试条件中,则将被转换为bool类型。
若是输入成功,则转换后的值为true,不然为false。
当用户输入的不是数字时,该程序再也不读取输入。
可使用cin输入表达式的值来检测输入是否是数字。程序发现用户输入了错误内容时,应采起3个步骤:
1. 重置cin以接受新的输入
2. 删除错误输入
3. 提示用户再输入
double fishes[MAX]; int i = 0; while(i < MAX){ while(!(cin >> fishes[i])){ cin.clear(); while(cin.get()!='\n') continue; cout << "error input, please go on: " << endl; } cout << fishes[i] << endl; i++; } return 0;
使用cin输入时,程序将输入视为一系列的字节,其中每一个字节都被解释为字符编码。无论目标数据类型是什么,输入一开始都是字符数据--文本数据。而后,cin对象负责将文本转换为其余类型。
对于输出,将执行相反的转换,即整数被转换为数字字符序列,浮点数被转换为数字字符和其余字符组成的字符序列(如284.53或-1.58E+06)。字符数据不须要作任何转换。
对于文件输入,C++ 使用相似于cout的东西。
* 必须包含头文件iostream
* 头文件iostream定义了一个用处理输出的ostream类;
* 头文件iostream 声明了一个名为cout的ostream变量(对象)。
* 必须指明命名空间std;
* 能够结合使用cout和<<来显示各类类型的数据。
文件输出与此及其类似。
* 必须包含头文件fstream
* 头文件fstream定义了一个用于处理输出的ofstream类。
* 须要声明一个或多个ofstream变量(对象)
* 必须指明命名空间std
* 须要将ofstream对象与文件关联起来,为此,方法之一是使用open()方法。
* 使用完文件后,应使用方法close()将其关闭
* 可结合ofstream对象和运算符<<来输出各类类型的数据。
char filename[20]; cout << "Enter the filename: " << endl; cin >> filename; ofstream infile; infile.open(filename); double test = 12.5; infile << test; infile.close();
* 重要的是,声明一个ofstream对象并将其同文件关联起来后,即可以像使用cout那样使用它。全部可用于cout的操做和方法(如<< 、 endl和self())均可以用于ofstream对象。
* 另外,close不须要使用文件名做为参数,由于infile已经同文件关联起来。若是忘记关闭文件,程序正常终止时将自动关闭它。
* infile可以使用cout可以使用的任何方法。不但能使用<<还可使用各类格式化方法,如self()和precision(),这些方法只影响调用它们的对象。
* open(),若文件不存在,将新建;若文件已经存在,丢弃原有内容,重写文件。
接下来介绍文本文件输入,它是基于控制台输入的,控制台输入涉及多个方面:
* 必须包含头文件iostream
* 头文件iostream定义了一个用于处理输入的istream类
* 头文件iostream声明了一个名为cin的istream对象
* 必须指明命名空间std
* 结合使用cin和>>来读取各类类型的数据
* 可使用cin和get()方法读取一个字符,使用cin和getline()来读取一行字符
* 能够结合使用cin和eof() fail() 方法来判断输入是否成功
* 对象cin自己被用做测试条件时,若是最后一个读取操做成功,它将被转换为布尔值true,不然被转换为false
文件输出与此及其类似:
* 必须包含头文件fstream
* 头文件fstream定义了一个用于处理输入的ifstream类。
* 须要声明一个或多个ifstream对象
* 必须指明命名空间std
* 须要将ifstream与文件关联起来,方法之一使用open()方法
* 使用完文件后,使用close()方法将其关闭
* 可结合使用ifstream对象和get()方法来读取一个字符,使用ifstream对象和getline()来读取一行字符。
* 能够结合使用ifstream和eof()、fail()等方法来判断输入是否成功
* ifstream对象自己被用做测试条件时,若是最后一个读取操做成功,它将被转换为布尔值true,不然被转换为false
ifstream outfile; outfile.open(filename); double test; outfile >> test; char test2[20]; outfile >> test2; cout << test << endl; cout << test2 << endl; outfile.close();
若是打开一个不存在的文件用于输入,将致使使用ifstream对象使用输入时失败。检查文件是否成功打开的首先方法就是使用is_open()。
outfile.open(filename); if (!outfile.is_open()) exit(EXIT_FAILURE);
若是文件被成功打开,方法is_open()返回true,不然,返回false;
函数exit原型在头文件cstdlib中定义的,在该头文件中,还定义了一个用于同操做系统通讯的参数值EXIT_FAILURE。函数exit()终止程序。
is_open是C++相对较新的内容。若是编译不过,可以使用较老的方法good()来代替。不过,方法good在检查可能存在的问题方面,没有is_open()那么普遍。
ifstream outfile; outfile.open(filename); if (!outfile.is_open()) exit(EXIT_FAILURE); int sum = 0; int number; outfile >> number; while(outfile.good()){ outfile >> number; sum += number; } if(outfile.eof()) { cout << "eof" << endl; } else if (outfile.fail()) { cout << "fail" << endl; } else { cout << "other" << endl; } outfile.close();
检查文件是否成功打开相当重要,好比:
* 指定的文件不存在;
* 文件可能位于另外一个目录中;
* 访问被拒绝;
* 输错了文件名或省略了文件扩展名。
程序读取文件时不该超过EOF。
* 首先,若是最后一次读取数据时遇到EOF,方法eof()返回true。
* 其次,程序可能遇到类型不匹配的状况,方法fail()返回true(若是遇到了EOF,该方法也返回true)。
* 最后,可能出现意外问题,如文件受损或者硬盘故障。
* 若是最后一次读取文件时放生了这样的问题,方法bad()将返回true。
不要分别检查这些状况,一个更简单的方法是使用good()方法,该方法在没有发生任何错误时返回true;
若是愿意,可使用其余方法来肯定循环终止的真正缘由:
if(outfile.eof()) { cout << "eof" << endl; } else if (outfile.fail()) { cout << "fail" << endl; } else { cout << "other" << endl; }
eof()只能判断是否到达EOF;
fail()能够检查是EOF仍是类型不匹配;
方法good()指出最后一次读取输入的操做是否成功,这一点相当重要。这意味着在执行读取输入的操做后,马上应用这种测试。为此,一种标准的方法是,在循环以前放置一条输入语句,并在循环的末尾放置另外一条输入语句。
outfile >> number; while(outfile.good()){ ++count; outfile >> number; sum += number; }
可对上述语句进行精简;
表达式outfile>>number的结果为outfile,而在须要一个bool值得状况下,outfile的结果为outfile.good()即,true或者false;
所以,能够改形成以下:
while(outfile >> number){ ++count; sum += number; }