C++浓缩(一)

4、复合类型:

* 复合类型(类除外)
* new和delete以及如何使用它们来管理数据
* string类(另一种处理字符串的途径)

4.1 数组

声明数组的通用格式是:

arrayType arrayName[arraySize]

arraySize必须是可知数据,可以使用 new 运算符来避开这种限制;

【注:】

1、编译器不会检查使用的下标是否有效。若对无效的下标进行赋值,编译器不会指出错误。
但程序运行后,赋值可能引发问题,它可能破坏数据或代码,也可能导致程序异常终止。so,必须确保只使用有效的下标值。

2、

sizeof是返回类型或者数据对象的长度(单位:字节);
如果sizeof用于数组名,得到的是整个数组的字节数;
如果用于数组元素,得到是元素的长度(单位:字节);

数组初始化的规则:

1. 只有在定义数组时可以初始化;然而,可以使用下标的方式来进行赋值;
2. 如果没有对定义的数组进行初始化,则元素的值是不确定的,意味着元素的值为以前驻留在该内存单元中的值;
3. 初始化时,若仅对一部分元素进行赋值,则其他的元素值为0;
4. 因此将数组中所有元素的值赋值为0,仅需要arrayName[500] = {0};
5. 如果初始化数组时,[]内的值为空,那么编译器将计算元素个数;

4.2 字符串

C++处理字符串的两种方式:

* C-风格字符串;
* string类;

一、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”实际上表示的是字符串所在的内存地址。

4.2.1 拼接字符串常量

任何两个由空白(空格、制表符和换行符)分隔的字符串常量将自动拼接成一个。【拼接时不会在被连接的字符串之间添加空格】

4.2.2 在数组中使用字符串

将字符串存储在数组中,两种方法:

* 将数组初始化为字符串常量
* 将键盘或者文件都入到数组中,通过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个字符的时候即结束,虽然数组中还有其他的字符。

4.2.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章进行介绍。

4.2.4 每次读取一行字符串输入

面向行的输入:

* cin类的getline
* cin类的get

getline丢弃换行符,get将换行符放入到输入队列中;

1、面向行的输入:getline()

通过回车输入的换行符来确定字符串的结束;

* 有两个参数,第一个参数表示要输入的数组名称,第二个参数代码要输入的字符串的长度;
* 如果参数为20,则该函数仅接受19个字符,剩余的一个字符用来存储在结尾处自动添加的空字符;
* getline在读取指定数目的字符或者遇到换行符的时候停止;

2、面向行的输入: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);

3、为什么使用get而不是getline呢?

* 因为老式实现没有getline;
* 怎么确定停止读取的原因是因为换行符或者是数组已经满了呢?查看下一个输入符,如果是换行符,说明已经读取了整行;如果不是换行符说明数组已经满了,该行还有其他的输入。

4.2.5 混合输入字符串和数字
#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类。

4.3 String类介绍

要使用string类,必须包含头文件string,string类位于命名空间std中。需要添加using编译指令;

string类与使用数组字符串有很多相似之处:
* 可以使用C-字符串风格来初始化string对象
* 可以使用cin将键盘输入存储到string对象
* 可以使用cout来显示string对象
* 可以使用数组表示法来访问存储在string对象中的字符

string对象与数组字符串的主要区别就是:
可以将string声明为简单变量;
类设计让程序自动处理string的大小;

4.3.2 赋值、拼接和附加

可以将一个string对象的值赋值给另一个string对象;可以使用+将两个字符串连接起来,可以使用+=将字符串附加到string对象的末尾;

4.3.3 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()

4.3.4 string类I/O

* 使用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;
}

运行结果如下:

134154_NWfr_724288.jpg

1. 未初始化的数组内容是未定义的;其次,strlen从数组的第一个元素开始计算字节数,直至遇到空字符;

2. 未初始化的string对象长度自动被设置为0;

3. C-风格字符串getline用法,cin.getline(charr, 20);

4. string getline用法,getline(cin, strr); 表明getline不是类方法,将cin作为参数,指出要到哪里查找输入,未指定长度,string对象会自动调整大小;

问题:为何一个getline是类方法,一个不是呢?

4.4 结构简介

结构:用户定义类型,可以存储多种类型的数据。

使用:

1. 定义结构描述--描述标记能够存储在结构中的各种数据类型

2. 按照描述创建结构变量

struct student {
        int age;
        string name;
 };

* 定义结构之后,就可以创建这种类型的变量了;

* C++ 允许在声明结构变量时省略struct关键字,结构标记的用法和基本类型的用法相同;

* 可以使用成员访问符(.)来访问成员;

eg:声明和初始化(逗号分割,并用花括号括起)

student stu1 = {
        24,
        "cql"
  };
4.4.1

定义结构类型的位置很重要:

* 定义在main函数中,内部声明仅可以被该声明所属的函数使用;

* 放到main的外面,位于函数外面的称为外部变量,外部声明可以被后面的所有函数访问;

C++ 也允许定义外部变量,但不提倡声明外部变量,提倡外部声明结构,另外,外部声明符号常量通常更合理。

4.4.3 

C++ 结构体允许将string作为成员吗?可以,只要可以编译通过。

4.4.4 其他结构属性

* 可以将结构作为参数进行传递,以及让函数返回一个结构;

* 使用赋值运算符(=)将结构赋值给另一个结构,即使结构中包含数组;这种赋值被称为成员赋值;

* 可以同时完成定义结构和声明结构变量

struct student {
        int age;
        string name;
    } stu1, stu2;

甚至可以以这种方式既声明又初始化:

struct student {
        int age;
        string name;
    } stu1 = {
        24,
        "cql"
    };

另外,C++的结构不仅仅可以有成员变量之外,还可以有成员函数。这点将在类中介绍

4.4.5结构数组

创建元素为结构的数组;

struct student {
        int age;
        string name;
    } stu1 = {
        24,
        "cql"
    };

初始化结构数组的规则可以结合初始化数组和初始化结构的规则。

由于数组中的每个元素都是结构,因此可以使用初始化结构的方式来提供值。最终结果为一个逗号分割,花括号括起来的列表。

4.5 共用体

一种数据格式,可以存储多个不同的数据类型,但同时只能存储其中的一种。

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;

由于共用体可以每次只可能存储一个值,因此它必须有足够的空间来存储最大的成员。所以,共用体的最大程度为其最大成员的长度。

4.6 枚举

。。。。。。

4.7指针和自由存储空间

计算机在存储数据时必须跟踪的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;

4.7.1 声明和初始化指针

计算机需要跟踪指针指向的值的类型,例如,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个字节(取决于具体的计算机系统)(有些系统可能需要更大的地址,系统可以针对不同类型使用不同长度的地址)。

4.7.2 指针的危险

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来创建动态数组

。。。。。。。。。。

5

5.1 for循环

5.1.1 for循环的组成部分

其中注意,测试表达式的值不一定为true或者false,可以使用任意表达式,C++将把结果强制转换成true或者false;

若为0,为false;若非零,为true;

C++程序会在需要整数值的时候将true和false转换为1和0,在需要bool值的地方将0和非零转换成false和true;

1、表达式和语句

(任何值)或者(任何有效的值和运算符的组合)都是表达式;每个表达式都有值

eg:10、22+27、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;

输出结果:

150359_cHEX_724288.png

通常,cout在现实bool值之前会将他们转换成int,但cout.setf(ios::boolalpha);函数设置了一个标记,

该标记命令cout显示true或者false,而不是1或者0;(当然,前提是运算结果是bool)

从表达式到语句的转换:只需要加上分号即可。

2、非表达式和语句

* 对任何表达式加上分号都可以成为语句

* 但语句去掉分号并不一样是表达式

3、修改规则

5.1.2 回到for循环
5.1.3 改变步长
5.1.4 使用for循环访问字符串
5.1.5 递增运算符(++)和递减运算符(--)
5.1.6 副作用和顺序点
5.1.7 前缀和后缀格式

前缀和后缀对执行速度有细微的差别;

* 前缀是将值加1,返回结果;

* 后缀首先复制一个副本,将值加1,返回副本;

相比而言,前缀效率比后缀效率高;

5.1.8 递增\递减运算符和指针

将*和++同时用于指针;怎么处理,取决于运算符的位置和优先级。

* 前缀递增、前缀递减和*优先级相同;从右到左的方式进行结合;

* 后缀递增、后缀递减的优先级比前缀运算符的优先级高;从左到右的方式进行结合;

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,使之指向第二个元素。

5.1.9 组合赋值运算符

-= += *=  /= %=

5.1.10 复合语句(语句块)

多条语句使用花括号括起来;

* 复合语句有一个有趣的特性,语句块中定义一个新的变量,则当程序执行该语句块中的语句时,该变量才存在。

执行完该语句块,变量将被释放。这表明此变量仅在该语句块中才是有用的。

* 若语句块中声明一个变量,外部声明也有一个重名的变量,该怎么处理呢?

* 在声明位置到内部语句块结束的范围之内,新变量隐藏旧变量

5.1.11 其他语法技巧---逗号运算符

逗号运算符允许将两个表达式放到只允许放一个表达式的地方。

    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;

* 逗号运算符的优先级是最低的;

5.1.12 关系表达式

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-风格字符串。

5.2 while循环
5.2.1 for 与while循环
5.2.2 等待一段时间,编写延时循环

。。。。

5.3 do和while循环
5.4 基于范围的for循环(C++11)
5.5 循环和文本输入

cin对象支持3中不同模式的单字符输入,其用户接口各不相同

5.5.1 使用原始的cin进行输入

cin忽略空格和换行符

    char a;
    int count = 0;
    cin >> a;
    while(a != '#'){
        count++;
        cin >> a;
    }
    cout << count << endl;
5.5.2 使用cin.get(char)进行补救

能够读取包括空格、制表符和换行符;

如果熟悉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;
5.5.3 使用哪一个cin.get()

cin.get()的三种使用方法:

    cin.get(arrayName, arraySize);
    cin.get();
    cin.get(ch);

函数重载,函数重载允许创建多个同名函数,条件式参数列表不同;

函数以不同的方式或针对不同类型执行相同的任务;

5.5.4 文件尾条件

。。。。。。。。。。。

5.6 嵌套循环和二维数组
5.6.1 初始化二维数组
5.6.2 使用二维数组

在此,仅介绍下声明城市数组的集中方式;

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;

6 分支语句与逻辑运算符

* 条件运算符;

* 逻辑运算符;

* 文件输入\输出;

6.1 if语句

和循环测试条件一样,if测试条件也被将值转换为bool值,因此0将被转换为false,非零为true。

6.1.1 if else 语句
6.1.2 格式化if else语句

对多条语句添加大括号;

6.1.3 if else if else 结构

6.2 逻辑表达式

逻辑OR || 逻辑AND && 逻辑NOT !

6.2.1 逻辑OR ||
    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已经进入的输入队列了。

6.2.2 逻辑AND &&
char * ch_ptr = "my god";
cout << ch_ptr << endl;

程序正常运行,但该方式不建议使用,已经被弃用

6.2.3 用&&来设置取值范围
6.2.4 逻辑NOT运算符!
6.2.5 逻辑运算符细节

逻辑OR和逻辑AND运算符优先级 < 关系运算符

!运算符优先级> 所有关系运算符和算术运算符>逻辑OR和逻辑AND

6.2.6 其他表示方式

* C++ 可用and 、or 和not;

* C可用and、or和not,前提:包含头文件iso646.h

6.3 字符函数库cctype

cctype:与字符相关的、方面的函数软件包;

函数名称返回值isalnum()如果参数是字母数字,即字母或数字,该函数返回trueisalpha()如果参数是字母,该函数返回真isblank()如果参数是空格或水平制表符,该函数返回trueiscntrl()如果参数是控制字符,该函数返回trueisdigit()如果参数是数字(0~9),该函数返回trueisgraph()如果参数是除空格之外的打印字符,该函数返回trueislower()如果参数是小写字母,该函数返回trueisprint()如果参数是打印字符(包括空格),该函数返回trueispunct()如果参数是标点符号,该函数返回trueisspace()如果参数是标准空白字符,如空格、进纸、换行符、回车

、水平制表符或者垂直制表符,该函数返回true

isupper()如果参数是大写字母,该函数返回trueisxdigit()如果参数是十六进制的数字,即0~9、a~f、A~F,该函数返回truetolower()如果参数是大写字符,则返回其小写,否则返回该参数toupper()

如果参数是小写字母,则返回其大写,否则返回该参数

什么是控制字符?

什么是打印字符?

6.4 ?:运算符

6.5 switch语句

switch(integer-expression) 
{
     case label1: statement(s)
     case label2: statement(s)
     ...
     default statement(s)
}

integer-expression必须是一个结果为整数值的表达式。

每个标签都必须是整数常量表达式。

最常见的标签是int或char常量、枚举量。

6.5.1 将枚举量用作标签

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;
    }
6.5.2 switch 和 if else

switch和if else相比,if else更通用。

switch的每个case标签值必须是单独的值且必须是整数(包括char)。

6.6 break和continue语句

6.7 读取数字的循环

假设要编写一个将一系列数字读入到数组中的程序,并允许用户在数组填满之前结束输入。

如果用户输入一个单词,而不是一个数字,情况将如何呢?发生这种类型不匹配的情况时,将发生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;

6.8 简单文件输入/输出

6.8.1 文件I/O和文本文件

使用cin输入时,程序将输入视为一系列的字节,其中每个字节都被解释为字符编码。不管目标数据类型是什么,输入一开始都是字符数据--文本数据。然后,cin对象负责将文本转换为其他类型。

对于输出,将执行相反的转换,即整数被转换为数字字符序列,浮点数被转换为数字字符和其他字符组成的字符序列(如284.53或-1.58E+06)。字符数据不需要做任何转换。

6.8.2 写入到文本文件中

对于文件输入,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(),若文件不存在,将新建;若文件已经存在,丢弃原有内容,重写文件。

6.8.3 读取文本文件

接下来介绍文本文件输入,它是基于控制台输入的,控制台输入涉及多个方面:

* 必须包含头文件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;
}

 

转载于:https://my.oschina.net/cqlcql/blog/634385

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值