c++ primer 第五版 翻译 第一章

许久未更新博客了,接下来更新的是c++ primer 第五版的翻译。
一来学习英语,二来巩固c++。

第一章开始
内容:
1.1写一个简单的c++程序
1.2初窥输入输出
1.3注释简介
1.4控制流程
1.5类简介
小结
专业术语

本章介绍c++基本概念:类型,变量,表达式,语句,函数。在介绍的同时,我们也会初略介绍怎么编译和执行一个c++程序。

通过本章学习,你能够编写,编译,执行一个简单的程序。在后面的章节,会认为你已经掌握了本章的基本内容,并在后续章节中详细解释这些内容。

学习编程语言的方法就是:写代码。在本章中,我们会写一个代码,来解决一个简单的书店问题。详细描述如下:

书店保存有所有交易的存档。每一个交易存档记录了一本书的销售信息.每个销售信息包含三个数据:

0-201-70353-X 4 24.99

第一个数据就是ISBN(国际标准书号,图书的唯一标识号).第二个代表卖出去了多少本.第三个是单价.书店老板时不时的读交易存档,然后计算销售量,销售总额,和平均售价.

为了有能力写这个书店程序,我们需要先学习几个c++的功能,并且,还需要知道怎么编译和执行程序.

尽管我们还没有开始设计我们的程序,但是,下面的东西是必须的:
1.定义变量
2.输入和输出
3.保存数据的数据结构
4.判断两个交易信息是否有相同的ISBN
5.使用一个循环来处理每一个交易信息
我们先学习怎么用c++来解决这些子问题,然后再写出我们的书店程序

1.1 写一个简单的c++程序

每个c++程序都包含一个或者多个函数,其中有个函数的名字,必须为main.因为操作系统运行每个c++程序都是从main函数开始.下面是一个简单的程序,它什么也不做,只是返回一个值给操作系统:

int main(){
    return 0;
}

定义一个函数,需要四个元素:一个返回值,一个函数名,一个参数列表(可以为空)—就是用小括号括起来的部分,一个函数体.尽管main是一个比较特殊的函数,但是它的构成和其他函数一样.

在这个例子中,main函数有一个空的参数列表(小括号括起来的部分).在6.2.5节我们将讨论main函数的其他参数类型

这个main函数需要一个int类型的返回值.int类型代表了一个整数类型.这个int类型是内置类型.什么是内置类型呢?内置类型就是这个语言定义的类型.

这个main函数的最后部分,是函数体,它由两个大括号括起来:

{
    return 0;
}

在两个大括号之间的语句只有return语句,这个语句用来结束一个函数.此处的return语句还返回了一个值给调用者.当return语句返回值时,返回值的类型必须和函数定义的返回值类型相匹配.上例中,mian函数的返回类型为int,return返回的值为0,0的类型为int.

注意:在return语句末尾的分号.分号是c++大部分语句的结束符.分号常常被忽略,一旦忽略分号,将会引起编译器的一些莫名其妙的错误.

在大多数的操作系统中,main函数的返回值通常表示了程序运行的状态.0表示运行成功,非零值表示发生了某种错误.不同的非零值代表不同的意思,非零值具体意义由操作系统定义.

关键概念:类型
类型是编程中最重要的基础概念之一,它将在本书中反复出现.一种类型定义了一个数据的内容和可以运用在内容上面的运算.

应用程序中使用的数据放在变量中,每一个变量都有一种类型.当一个变量v的类型是T的时候.我们常说”v的类型是T”,或者,”v是一个T类型变量”

1.1.1 编译并执行程序

写完上述的程序之后,我们需要编译它.那么,怎么根据你的操作系统和编译器来编译上述程序呢​?关于你使用的特定的编译器怎么工作的,你得翻阅相应的参考手册,或者询问对这个比较了解的同事.

大多数的pc编译器都可以直接从IDE里面运行,IDE就是集成开发环境,它将编译器和一些分析工具绑定在一起,构成一个开发环境.在开发大型程序的时候,IDE环境是非常有帮助的.但是需要花费时间来学习,如何高效的使用IDE已经操出了本书的范围.

大多数的编译器(包括在IDE里面的编译器)都提供了一个命令行的接口.除非你已经知道了怎么使用IDE,否则,对于新手来说,使用命令行会更加容易一些.使用命令行可以让你更加关注c++的学习,一旦你理解了c++这门语言,IDE也就不难了.

程序源文件命名约定

无论你使用命令行还是IDE,大多数的编译器都希望源代码被放置在一个或者多个文件中.程序文件通常称为源文件.在大多数的操作系统中,一个源文件的名字都有一个后缀.这个后缀是一个句号开始,后面跟着一个或者多个字符.不同的编译器使用不同的后缀,最常见的有下面几个:.cc .cxx .cpp .cp .C

从命令行运行编译器

如果你使用命令行这种方式,那么我们通常是在控制台里面编译程序.(例如,unix系统里面的shell,windows系统里面的cmd).假如我们的main程序在prog1.cc文件中.我们可以使用下面的命令来编译它:

$ CC prog1.cc

此处,CC表示了编译器的名字,$是系统提示符.上述命令执行完毕之后,编译器产生一个可执行文件.在windows系统中,可执行文件的名字为:prog1.exe.而在unix中,通常为a.out

在windows上面运行一个可执行文件,需要提供一个可执行文件的名字,并且可以省略.exe的后缀名,如下:

$ prog1

在某些系统下,你必须显示的指定可执行文件的路径,尽管这个可执行文件就在当前目录下.此种情况,示例如下:

$ .\prog1

点号后面跟一个反斜杠,表示这个文件在当前目录下.

在unix上面运行可执行文件,需要给出整个文件名,包括后面的扩展名,如下:

$ a.out

如果需要给出文件的位置,我们可以使用点号加斜杠来表示文件在当前目录下,示例如下:

$./a.out

main返回值的读取依赖于系统.在unix和windows系统中,程序运行完成后,你可以使用一个相应的echo命令来查看返回值.

在unix系统中,返回值可以通过下面来查看

$ echo $?

在windows系统中,返回值可以通过下面来查看

$ echo  %ERRORLEVEL%

运行GNU或者微软编译器
运行c++编译器的命令,会因为不同的操作系统和不同的编译器而不同.最常用的编译器是GNU编译器和微软的Visual
$ g++ -o prog1 prog1.cc 此处,$是系统提示符,-o prog1
是编译器的参数,指明了可执行文件的名字.这个命令产生一个可执行文件,名字为prog1,或者prog1.exe.unix系统中为prog1,windows系统中为prog1.exe.
如果-o prog1这句话没有,那么在unix系统上的可执行文件为a.out,在windows系统上的可执行文件为a.exe.(注意:根据你使用的GNU编译版本,你可能需要使用-std=c++0x去打开编译器对c++11标准的支持) 微软的Visual Studio2010编译器是c1: c:\User\me\Programs> c1 /Ehsc prog1.cpp
此处:c:\User\me\Programs>是系统提示符,\User\me\Programs>是当前目录.c1调用编译器,/EHsc是编译器选项,表示:打开标准异常处理.微软的编译器自动生成一个可执行文件,这个可执行文件以第一个源文件名为名字,并以.exe为后缀.此例中,可执行文件名为:prog1.exe

编译器通常有一些选项,用来对有问题的程序,给出相应的警告信息.使用这些选项是一个好的习惯.在GNU编译器中选项为-Wall,在微软编译器中选项为/W4.

1.2 初窥输入输出

c++语言没有定义任何用来输入和输出的语句.但是,它包含一个标准库,这个库提供了输入输出功能(以及其他许多的功能).对于大部分业务来说,我们只需要了解IO库少量的概念和操作即可,不需要了解IO库的整个功能和用法(包括本书中的例子).

本书中的大多数例子,都使用了iostream库.iostream库的基础由两类库组成,分别是istream和ostream.他们分别代表了输入流和输出流.流代表了来自于IO设备的字符序列或者写入IO设备的字符序列.术语”流”表明:字符随着时间的推移,在生成和消耗.

标准输入输出对象
这个iostream 库,定义了四个IO对象.为了操作输入,使用类型为istream,名字为cin的对象(cin发音为see-in).这个对象也被当做标准输入对象.
为了操作输出,使用类型为ostream,名字为cout的对象(cout发音为see-out),该对象也被当作标准输出对象.
iostream库也定义了两个其他的ostream对象,分别为cerr和clog(发音分别为see-err和see-log).通常我们把warning,error信息,往cerr里面输出.因为cerr当作标准错误输出.而对于程序执行期间的常用信息,则往clog里面输出.

通常情况下,系统将这些对象与程序执行的窗口关联起来.因此,当我们使用cin来获取输入的时候,数据将从程序执行的窗口中获取数据.同样的,当我们向cout,cerr,clog输出时,这些输出都将写入到与输入相同的窗口中.

一个使用了IO库的程序

在我们的书店程序中,我们需要将几个记录合并成一个.为了简单起见,首先来看看如何将两个数相加.我们使用IO库,来扩展我们的main程序,该程序提示用户,输入两个数,然后输出他们的和.

#include <iostream>
int main(){
    std::cout<<”Enter two numbers:” << std::endl;
    int v1=0,v2=0;
    std::cin >> v1 >> v2;
    std::cout << “The sum of ” << v1 << “ and ” << v2
        << “ is ”<< v1+v2<< std::endl;
    return 0;
}

程序在屏幕上输出:
Enter two numbers:
然后等待用户输入两个数据.如果用户输入:
3 7
然后回车键.则,这个程序将产生下面的输出:
The sum of 3 and 7 is 10
上面这个程序的而第一行为:

  #include<iostream>

这个语句告诉,编译器,我们想使用iostream库.放在尖括号里面的名字被当作头文件处理(此处为iostream).如果一个程序要使用相应的库,必须包括相应的头文件.include指令必须写在单行上,头文件必须和include写在同一行上.通常情况下,include指令必须写在所有函数之外.一般情况下,我们将include指令放源文件的开头处.

向一个流中写入数据

在main函数的第一个语句执行了一个表达式.c++里面表达式产生一个结果,表达式由一个或者多个操作数和一个操作符(通常情况下为一个操作符)组成.在这个语句里面的表达式,使用了输出运算符(<<运算符)向标准输出打印一条信息.

std::cout << “Enter two numbers:” << std::endl;

运算符<<使用两个操作数:左侧操作数必须是ostream对象.右侧操作数是需要被打印的值.运算符<<将给定的值,输出到给定的ostream对象上.输出运算符的返回值为,左侧的操作数.既,返回值为左侧的ostream对象.

我们的输出语句使用了输出运算符两次.因为第一个输出运算符的返回值,作为了第二个输出运算符的左侧操作数.由此,我们可以将输出语句全部链接起来.因此,我们的表达式等价于:

(std::cout<<Enter two numbers:”) << std::endl;

在这个链上面的每一个运算符,都有相同的左侧操作数.此处为std::cout.另外,我们也可以使用两个语句来产生相同的输出:

std::cout << “Enter two numbers:”;
std::cout << std::endl;

第一个输出运算符打印一个消息给用户,这个消息是一个字符串字面值.字符串字面值就是:包裹在双引号的字符序列.在双引号之间的文本将被输出到标准输出.

第二个运算符,输出endl,它是被称作“机械手”的一个特殊值(manipulator具体翻译不来,我猜想很可能是以前打字机时代,每次换行使用的一个特殊器件).endl的效果是结束当前行并将缓存中的值刷新到设备中.刷新缓存可以保证:程序产生的所有输出,都被写入输出流中,而不是停在内存中等待被写入.

警告:
在调试的时候,程序经常增加打印语句.这类语句,应该总是刷新流.否则,一旦程序崩溃,输出可能还停留在缓存中,导致得出关于程序崩溃的错误结论.

使用标准库中的名字

细心的读者将会注意到:程序使用std::cout 和std::endl而不是直接使用cout和endl.前缀std::表明,cout和endl被定义在一个叫做std的命名空间中.命名空间可以帮助我们避免命名冲突.所有的在标准库中的名字,都被定义在了std命名空间中.

一个命名空间库的使用,会产生一个副作用,那就是,当我们使用这些库中的名字的时候,我们必须显示的声明库中的名字.std::cout使用了作用域运算符(::),它表明,我们想使用被定义在std命名空间中的cout.3.1节将展示一种访问这些变量的简便方法.

从流中读数据

已经请求了用户输入之后,我们需要从输入中读取数据.我们先定义了两个变量来保存输入的数据:

int v1=0,v2=0;

我们定义了这些变量为int型.int型是一种内置类型,代表整数.我们也初始化他们为0.在变量创建的时候,我们指定一个值给它,此时,就是变量的初始化.
下一个语句:

std::cin >> v1 >> v2;

从输入中读取数据.输入运算符(>>)与输出运算符类似.它使用一个istream作为左侧操作数,并使用一个对象作为右操作数.输入运算符将从给定的istream中读取数据,并将读到的数据存储在右侧的对象中.跟输出运算符一样,输入运算符,也将左侧的操作数作为整个表达式的返回值.因此,这个表达式等价于:

(std::cin >> v1 ) >> v2;

因为输入运算符返回它的左侧操作数.我们可以链接多个输入语句成一个单一的语句.我们的语句从std::cin里面读两个值.将第一个值存储在v1里面,第二个值存储在v2里面.换句话说,我们的输入操作,等于如下:

std::cin >> v1;
std::cin >> v2;

完成程序
剩下的就是打印我们的结果

std::cout << “The sum of ” << v1 <<and<< v2 
        << ”is ” << v1+ v2 <<std::endl;

这个语句,尽管比提示用户输入的语句要长,但是他们原理是相似的.它向标准输出,打印每一个操作数.本例中,有意思的是:操作数并不是相同类型的值.一些操作数是字符串字面量.例如”The sum of ”.其他的有int类型的值,如v1,v2;有算数表达式的值,如v1+v2.标准库定义了多个输入输出版本,用来处理这些不同类型的操作数.

1.3 注释简介

在我们的程序变得复杂之前,我们应该学习,怎样处理注释.注释有助于阅读程序.注释被用来简述一个算法,表明一个变量的目的,或者使一个模糊的代码段更清晰.编译器会忽略注释,因此注释不会对程序的行为和性能有任何影响.

尽管编译器忽略了注释,但是程序员不应该忽略他们.当系统文档过期的时候,程序员甚至更愿意相信注释.一个错误的注释,比没有注释更烂,因为这样会导致阅读者的误解.当你更新了你的代码时,一定要记得更新你的注释.

C++ 注释的种类

c++有两种类型的注释:单行注释和注释对(paired).单行注释:以双斜杠开始(//),以换行符结束.在双斜杠右边的所有字符,都将当作注释被编译器忽略掉.这种类型的注释,可以包含任何文本,包括两个双斜杠.
另外一种注释是使用两个分隔符(/*和*/),这种注释是从c当中继承过来的.这种类型的注释以一个/*开始,然后以一个*/结尾.这种注释除了*/以外,可以包含任何文本,包括换行符.编译器将/*和*/之间的所有字符都当作注释.

一个注释对,可以被放置在任何tab,空格,换行符放置 的地方.注释对可以跨越多行,但是这个不是必须的.当一个注释对跨越多行时,将多行注释里面的每一行都显式的表明这行是注释,通常是一个非常好的习惯.我们常用的方法是:在每一行的开头使用一个星号,以表明是多行注释的一部分.

程序通常混合的使用这两种注释.注释对通常用来多行注释;双斜杠通常用来半行,单行注释.

#include <iostream>
/*
* simple main function:
* read two numbers and write their sum
*/
int main(){
    //prompt user to enter two numbers
    std::cout << “Enter two numbers:” << std::endl;
    int v1=0,v2=0;      //variables to hold the input we read
    std::cint >> v1 >> v2;  //read input
    std::cout << “The sum of ” << v1 << “ and ” << v2
            << “ is ”<< v1+v2<<std::end;
    return 0;
}

注意:
在这本书里面,我们给注释斜体字,为的是在普通的程序文本中更加突出.在实际使用中,注释的字体是否与普通的程序文本有区别,依赖于你使用的编程环境.

注释对不能嵌套

注释对以/*开始,以*/结尾.因此,一个注释对不能出现在另一个注释对的里面.对于这种错误,编译器报告出来的信息,可能比较晦涩.举个例子,在你的系统中编译下面的程序:

/*
 * comment pairs /*   */ cannot nest.
 * ''cannot nest'' is considered source code,
 * as is the rest of the program
 */
int main()
{
    return 0;
}

在调试时候,我们经常需要注释掉一块代码.因此,代码可能嵌套注释对.对于注释掉一大块代码,最好的方式是:每一行使用单行注释.如下:

// /*C++ Primer, Fifth Edition

// * everything inside a single-line comment is ignored
// * including nested comment pairs
//  */

1.4 控制流

通常情况下程序顺序执行:在语句块里面的第一个语句被执行,然后第二个,依次类推.当然少数程序只需要这种顺序执行即可(包括我们用来解决书店问题的程序).然而,c++提供了几种控制流语句,这些语句可以构建更复杂的执行流程.

1.4.1 while语句

只要给定的条件为true,while语句就重复的执行代码段.我们可以使用while语句写一个程序来计算1到10之间的和,如下:

#include <iostream>
int main(){
    int sum=0,val =1;
    //keep executing the while as long as val is less than or equal to 10
    while(val <= 10){
        sum += val; //assigns sum + val to sum
        ++val; //add 1 to val
    }
    std::cout <<Sum of 1 to 10 inclusive is ”
            << sum << std::endl;
    return 0;
}

当我们编译并执行这个程序的时候,它打印如下信息:

Sum of 1 to 10 inclusive is 55

像以前一样,以#include 头文件开始,然后定义main函数.在main函数里面,我们定义了两个int类型的变量:sum和val,sum保存所有值的总和.val代表从1到10的每一个值.我们初始化sum为0,并且val从1开始.

这个程序新的部分是while语句.一个while语句有下面的格式:

while(condition)
        statement

一个while的执行:先判断condition,然后再执行相应的statement.然后再次判断condition,然后再次执行statement,依次类推,直到condition为false.一个condition是一个表达式(后文称之为条件表达式),他会生成一个结果,不是true就是false.一旦condition的结果为true,statement就会被执行.statement被执行完之后,再次判断condition的结果.如果condition为true,则statement再次被执行.while语句继续测试condition然后执行statement,直到condition为false.

在这个程序里面,while语句是:

//keep executing the while as long as val is less than or equal to 10
while(val <= 10){
    sum += val;//assigns sum + val to sum
    ++val; //add 1 to val
}

此处condition使用小于等于运算符(<=)去比较val的值和10的大小.只要val小于等于10,则返回值为true.如果返回值为true,则执行while的循环体.在这个例子,这个这个循环体是两个语句:

{
    sum+=val;//assigns sum + val to sum
    ++val;  //add 1 to val
}

语句块是由大括号包围起来的,0个或者多个语句.一个语句块可以被当作一个语句来使用,它可以出现在普通语句出现的地方.在这个语句块里面的第一条语句使用了复合赋值运算符(+=).这个运算符将它的右侧的操作数,加上左侧的操作数,然后将结果存储在左侧的操作数中.它和编写加法然后赋值,在本质上是相同的.

sum = sum + val;//assign sum + val to sum

因此,第一个语句是将val的值与sum相加,然后将结果存储在sum中.
下一条语句:

++val; //add 1 to val

使用了前置自增运算符(++).自增运算符增加1到它的操作数中.++val与val = val+ 1是一样的.

执行了while的循环体之后,再次判断condition的结果.如果val的值仍然小于等于10,则while的循环体将会再次被执行.循环继续,再次判断condition并执行循环体,直到val的值不再小于等于10.

一旦val的值大于10.程序退出while循环体,然后继续执行接下来的语句.在这个例子中,接下来的语句是打印我们的输出.然后接着执行return语句.

1.4.2 for语句

在我们的while循环中,我们使用了val去控制我们执行多少次循环.我们判断val的值,然后在循环体中,让val自增.

这种写法(在条件判断中使用一个变量,然后在循环体中自增)在编程中太常见了,因此c++定义了第二种循环语句—–for语句.for语句可以使用下面的模板来简化代码.我们可以通过使用for语句,来重写我们的程序.

#include <iostream>
int main(){
    int sum=0;
    //sum values from 1 through 10 inclusive
    for(int val=1;val <= 10;++val)
        sum +=val;//equivalent to sum = sum +val

    std::cout << “Sum of 1 to 10 inclusive is ”
            << sum << std::endl;
    return 0;
}

跟以前一样,我们定义了sum并且初始化它为0.在这个版本中,我们定义了val作为for语句自身的一部分:

for(int val = 1;val<=10;++val)
    sum += val;

每一个for语句有两部分:一个头部,和一个循环体.头部控制循环体被执行多少次.头部包含三部分:一个初始化语句,一个条件语句和一个表达式.在这个例子中,初始化语句为:

int val=1;

定义了一个名为val的int变量,并初始化为1.变量val只能存在于for语句内,在for语句之外不能使用val变量.初始化语句只会被执行一次,它是for语句的入口.条件语句

val < = 10

将val值于10相比较.条件语句在每次循环中都会被执行.只要val的值小于等于10,那么就执行for的循环体.
表达式在循环体被执行之后,才执行,此处,表达式为:

++val;

使用前置自增运算符,该运算符将val的值加1.执行了表达式之后,for语句再次判断条件表达式的结果.如果val的新值仍然小于等于10,然后for的循环体将再次被执行.执行之后,val的值再次被自增.循环继续,直到条件表达式返回值为false.

在这个for循环体中,执行加法操作:

sum += val;//equivalent to sum = sum +val

这个for语句的执行流程概述如下:
1.创建val变量并初始化为1
2.判断val是否小于等于10,如果是,执行for的循环体.如果不是,退出循环,然后继续执行for下面的语句
3.自增val的值
4.重复第二步,只要条件判断为真,则继续执行后续的步骤

1.4.3 读取数量不定的输入

在前面的段落里面,我们写了一个程序用来计算1到10之和.这个程序的一个扩展是:让用户输入一些列的数字,然后计算他们的和.在这种情况下,我们不知道有多少数字需要被求和.因此,我们需要一直读直到没有数据可读.

#include <iostream>
int main{
    int sum =0,value =0;
    //read until end-of-line ,calculation a running total of all values read
    while(std::cin >> value)
        sum += value;//equivalent to sum = sum +val
    std::cout << “Sum is : ”<< sum << std::endl;
    return 0;
}

如果我们给程序输入如下数据:
3 4 5 6
然后程序输出如下:
Sum is :18

在main函数里面,定义了两个int类型的变量,分别为sum和value.他们都被初始化为0.我们使用value来保存我们读取到的数.我们读数的语句放在了while语句的条件语句中

while(std::cin >> value)

当执行while的条件语句的时候,将会执行下面的表达式:

std::cin >> value

该语句从标准输入中读取数据,然后将读到的数据存储在value变量中.输入运算符返回它的左侧操作数,此例中,为std::cin.因此,while的条件表达式为,判断std::cin

当我们使用istream对象做为条件表达式的时候,实际上是判断流的状态.如果流是有效的,即,流没有遇到错误,那么对istream的判断就是返回true.当遇到了文件结束符,或者一个非法的输入.例如读了一个不是整数的值,那么istream的状态为无效状态.一个无效状态的istream将造成条件表达式产生false.

因此,我们的while执行,一直到遇见一个文件结束符(或者错误输入)才退出.while的循环体使用了复合赋值运算符来相加value和sum.一旦条件为false.while循环将退出.然后执行下一条语句,打印sum的值.

从键盘里面键入文件结束符

当从键盘向程序输入的时候,对于文件结束符,不同的操作系统有不同的规定.在windows系统中,我们通过键入control-z来表示文件结束符——按住Ctrl键,然后按下z键,接着按下enter键或者return键.在unix系统中,包括Mac OS x,文件结束符通常为control-d

再探编译

编译器的部分工作就是寻找代码中的错误.那么编译器能够检查什么呢?它不能够检查程序员的意图,逻辑.他能够检查程序的语法格式.下面是编译器能够检查出的常见的错误种类:
语法错误:程序员在c++编程中的语法性错误.下面程序展示了常见的语法错误,每一个注释都描述了它下一行的错误.

//错误:对于main来说,参数列表缺少)
int main({
    //错误:endl后面使用了冒号,而不是分号
    std::cout  << “Read each file.” << std::endl:
    //错误:字符串字面量,缺少双引号
    std::cout << Update master. << std::endl;
    //错误:第二个输出运算符缺失
    std::cout << “Write new master.”  std::endl;
    //错误:return语句缺少;
    return 0
}

类型错误:每一个数据项都有一个对应的数据类型.例如,10的类型为int;单词”hello”,是一个字符串字面量(包括两边的双引号).类型错误的例子有:给一个带int类型参数的函数,传递一个字符串字面量.
声明错误:在c++里面,每一名字在使用之前,必须先声明.名字声明失败通常会产生一条错误信息.两个最常用的声明错误:
1.在使用标准库中的名字时,忘记使用std.
2.对于一个标识符拼写错误:

#include <iostream>
int main(){
    int v1 = 0,v2=0;
    std::cin >>v >>v2;//错误:使用了v而不是v1
    //错误:cout没有被定义,应该是std::cout
    cout << v1+v2<<std::endl;
    return 0;
}

错误信息通常包含一个行号和关于错误的简短描述.按照报告错误的顺序,纠正错误,是个好习惯.通常一个错误,会造成一些列的影响,导致编译器报出比实际多得多的错误.在每修改一个错误之后,或者在修改一小部分可见错误之后,再次编译代码是一个好习惯.这就是所谓的编辑-编译-调试

1.4.4 if语句

像其他大多数的语言一样,c++提供了if语句用于条件执行.我们可以使用if语句来计算每个值连续出现的次数.

#include <iostream>
int main(){
    //curVal is the number we’re counting;we’ll read new values into val
    int currVal =0,val =0;
    if(std::cin >> currVal){
        int cnt = 1;//store the count for the current value we’re processing
        while(std::cin >> val){//read the remaining numbers
            if(val == currVal) //if the values are the same
                ++cnt;
            else{//otherwise ,print the count for the previous value
                std::cout << currVal << “ occurs ”
                        << cnt << “ times ” << std::endl;
                currVal = val; //remember the new value
                cnt = 1;
            }
        }//while loop ends here

    //remember to print the count for the last value in the file
    std::cout << currVal << “  occurs ”
            << cnt << “  times ”<< std::endl;
    }//outermost if statement ends here
return 0;
}

如果我们给如下的输入:
42 42 42 42 42 55 55 62 100 100 100
那么输出如下:
42 occurs 5 times
55 occurs 2 times
62 occurs 1 times
100 occurs 3 times
这个程序的大部分代码都和以前的代码类似.我们首先定义了val和currVal:currVal 用来保存当前正在计算的数;val保存每一个从标准输入读到的数.新增部分是两个if语句.第一个if语句如下:

if(std::cin >> currVal){
    //...
}//outermost if statement ends here

它是为了保证输入不会为空.跟while一样,if也计算条件表达式.在第一个if语句里面的条件表达式是读取值到currVal中.如果读取成功,条件表达式为true,接着执行if的语句块.if的语句块是紧跟在if后面的左大括号,和return语句之前的右大括号.

一旦我们知道有数字需要计算,我们定义一个cnt变量,该变量用来统计每个数字出现了多少次.我们使用了一个,跟前述类似的一个while循环,它将标准输入的值读出来.

while循环体包含第二个if语句

if(val == currVal) //if the values are the same
    ++cnt;  //add 1 to cnt
else{//other wise .print the count for the previous value
    std::cout << currVal << “  occurs ”
            << cnt << “ times ” << std::endl;
    currVal = val;  //remember the new value;
    cnt =1 ;    //reset the counter
}

在这个if语句里面,我们使用了等于运算符(==),等于运算符用来判断当前的val是否等于currVal.如果等于,则执行跟在条件表达式后面的语句.这个语句就是对cnt变量进行自增.这句话表明currVal出现一次.

如果条件表达式为false,即,val不等于currVal.我们就执行跟在else后面的语句.else后面的语句是一个代码块,这个代码块由一个输出语句和两个赋值语句组成.输出语句用来打印我们处理完了的计数值.赋值语句将cnt赋值为1,将currVal赋值为刚刚读取到的新值.

警告:
c++使用=作为赋值运算符,使用==作为等于运算符.两个运算符都可以出现在条件表达式里面.当你想在条件表达式里面使用==,却失误,使用了=,这是一种常见错误.

关键概念:c++程序的缩进和格式
c++程序很大程度上是格式自由的,这就意味着:我们将大括号,缩进,注释,换行符放在哪里对于程序的语义没有任何影响.例如,作为main函数体开始的大括号,可以放在main相同的行上,也可以放在下一行,还可以放在任何你喜欢的其他行上.唯一的要求是:大括号必须是跟在main参数列表之后的第一个非空,非注释字符.

尽管我们可以使用我们自己喜欢的格式进行编程,但是,我们使用的那些格式会影响程序的可读性.例如,我们可以将main函数写在一行上,虽然这种写法是合法的,但是这样阅读起来非常困难

对于c/c++程序正确的格式,一直都是争吵不断.我们相信没有一种单一的正确格式,但是在风格上保持一致是非常有价值的.大多数程序员缩进他们程序的各个组成部分,正如我们在main函数体和循环体里面缩进语句一样.
对于限定函数体界限的大括号,我们倾向于将其放在单独的行中.我们还习惯将复合的IO表达式进行缩进,目的是使运算符排列整齐.其他的缩进规则也会使复杂的程序,变得更加清晰

我们时刻牢记一件事:程序的其他格式是存在的.当你选择了一种格式之后,你应该思考这种格式是如何影响程序的可读性和易理解性.一旦你选择一种风格,你应该坚持使用这种风格.

1.5 类简介

在解决我们的书店程序之前,剩下的唯一一个问题是:如何定义个数据结构来代表我们的交易数据.在c++里面,我们通过定义一个类来定义自己的数据结构.一个类定义了一种类型,以及相关联的一组操作.类机制是c++里面最重要的特性之一.事实上,c++程序的主要聚集点是:定义类类型,让它具有跟内置类型一样的行为.

在本段中,我们描述一个简单的类,然后使用这个类来写我们的书店程序.我们会在后续,学习了更多的类型,表达式,语句,和函数之后,来实现这个类.

为了使用一个类,我们需要知道三个东西:
1.它的名字是什么
2.它被定义在什么地方
3.它支持那些操作

对于我们的书店程序,我们假定这个类叫做Sales_item,并且它已经定义在了Sales_item.h头文件中了.

正如所见,为了使用一个库,我们必须包含相应的头文件.同样的,对于我们自己的程序,我们使用头文件去获取已经定义了的类.
通常情况下,头文件的名字来源于文件内的类名.头文件通常有一个.h后缀,但是一些程序员使用.H,.hpp或者.hxx作为后缀.一般情况下标准库文件不在使用后缀.编译器通常并不关心头文件名字的格式,但是一些IDE可能会关心.

1.5.1 Sales_item 类

Sales_item类的目的是代表,销售额,销售数,和单价.这些数据如何存储和如何计算不是我们关心的.使用这个类,我们也不用关心它是怎么实现的,我们只需要关心这种类型的对象可以执行那些操作.

每一种类都定义了一种类型.这种类型的名字和类名一样.因此,我们的Sales_item类定义了一种叫做Sales_item的类型.跟内置类型一样.我们可以定义这种类型的一个变量,
Sales_item item;
除了能够定义Sales_item类型的变量,我们还可以:
1.调用isbn的函数,从Sales_item对象中获取ISBN号
2.使用输入(>>)输出(<<)运算符去读取Sales_item类型的对象
3.使用赋值运算符(=)将一个Sales_item对象赋值给另外一个
4.使用相加运算符(+)将两个Sales_item对象相加.这两个对象必须是ISBN号相同.结果是一个新的Sales_item对象,它的ISBN是它操作数的ISBN,它的销售数,和销售额是两个操作数的和.
5.使用复合赋值运算符(+=)将一个Sales_item对象加到另外一个对象上.

关键概念:类定义了行为

当你阅读这些程序的时候,一定要牢记:Sales_items类的作者定义了,这个类能够执行的所有操作.换句话说,Sales_item定义了,对象创建的时候做什么操作.对象被赋值,相加,输入,输出的时候做什么操作.

通常情况下,类的作者定义了该类能够使用的所有操作.现在我们仅知道Sales_item类上面能够执行的操作是我们在这节列出来的那些操作.

读写Sales_item

既然我们已经知道可以在Sales_item上面能够执行这些操作,那么我们可以写一个程序来使用这个类.例如:下面的程序从标准输入中读取数据进Sales_item,然后将Sales_item输出到标准输出中.

#include <iostream>
#include “Sales_item.h”
int main(){
    Sales_item book;
    //read ISBN number of copies sold,and sales price
    std::cin >> book;
    //write ISBN number of copies sold,total revenue,and average price
    std::cout << book << std::endl;
    return 0;
}

如果我们将下面数据作为输入:
0-201-70353-X 4 24.99
那么程序的输出将是:
0-201-70353-X 5 99.96 24.99

输入表示:我们卖了4本书,每本$24.99.我们的输出表示:总共卖了4本书,销售额为$99.96,单价为$24.99

这个程序开始以两个include指令开始.其中一个使用一种新的格式。来自于标准库中的头文件被放在了尖括号之内.不是标准库中的头文件放在了双引号之内.

在main函数中我们定义了一个对象,名字叫做book.这个对象用来保存从输入读取到的数据.下一条语句将数据读进对象中.第三条语句将这个对象打印到标准输出中.

Sales_item相加

一个更有趣的例子是两个Sales_item相加

#include <iostream>
#include “Sales_item.h”

int main(){
    Sales_item item1,item2;
    std::cin >> item1 >> item2; //read a pair of transactions
    std::cout << item1+item2 << std::endl; //print their sum
    return 0;
}

如果我们给这个程序下面的输入
0-201-78345-X 3 20.00
0-201-78345-X 2 25.00
程序的输出为:
0-201-78345-X 5 110 22
这个程序开始处包含Sales_item和iostream两个头文件.接下来我们定义了两个Sales_item对象来保存交易记录.我们从标准输入中读入需要的数据.输出表达式将两者相加,然后再输出结果.

值得注意的是:这个程序和以前写的程序是多么的相似.我们输入两个数,然后输出他们的和.造成如此相似是因为,我们将读和打印的整形,换成了Sales_item类型.
对于int的情况,我们使用了传统意义上的和——将两个数相加.
对于Sales_item的情况,我们使用了一种新概念上的和——-将两个Sales_item对象的各个组成成员相加

使用文件重定向

向你的测试程序重复的输入这些交易记录,是非常乏味的.大多数操作系统支持文件重定向.文件重定向可以让我们将一个命名的文件于标准输入和标准输出相关联:
$ addItems < infile > outfile
$是系统提示符,并且我们的相加程序已经被编译成了一个可执行文件,叫做addItems.exe(或者在unix系统上叫做addItems).这个命令将从infile文件中读入交易记录,然后将输出写入当前目录下的outfile文件中.

1.5.2 初窥成员函数

两个Sales_item相加的程序应该,检查是否两个对象有相同的ISBN.我们可以按如下做:

#include <iostream>
#include “Sales_item.h”
int main(){
    Sales_item item1,item2;
    std::cin >> item1 >> item2;
    //first check that item1 and item2 represetn the same book
    if(item1.isbn() ==  item2.isbn()){
        std::cout << item1+ item2 << std::endl;
        return 0;//indicate success
    }else{
        std::cerr << “Data must refer to same ISBN”
                << std::endl;
        return -1;//indicate failure
    }
}

这个程序与上一个版本的不同是:if语句,以及if语句的else分支.尽管我们没有明白if语句的条件表达式,但是我们能明白程序的执行.如果条件表达式为true,我们的输出就跟以前一样,然后返回一个0,返回0表示执行成功.如果条件表达式为false,我们执行else后面的语句块,这个语句块打印一条信息然后返回一个错误标识

什么是成员函数

if的条件表达式:

item1.isbn() == item2.isbn()

调用了一个成员函数,叫做isbn.成员函数就是:一个类里面定义的函数,这个函数做为类的一部分.成员函数有时也被叫做方法.

通常,我们以一个类的名义来调用一个成员函数.例如,等号运算符的左侧:
item1.isbn
点运算符(“.”运算符)表示:我们需要item1对象的isbn成员.点运算符只能运用于类类型的对象上.点运算符左侧操作数必须是一种类类型,右操作数必须是这个类型的成员.
点运算符的结果是右操作数对应的成员.

当我们使用点运算符去获取一个成员函数的时候,我们通常是想调用该函数.我们调用一个函数使用调用运算符(()运算符).调用运算符是一对小括号,在小括号中是参数列表.isbn成员函数不需要参数,因此item1.isbn(); 表示调用item1对象的isbn成员函数.这个函数返回值为:存储在item1里面的ISBN号.

在等于运算符的右侧操作数与左侧一样.它返回存储在item2中的ISBN号.如果ISBN号相同那么条件表达式为true,否则为false

1.6 书店程序

现在,我们已经准备好,解决最开始的书店程序了.我们需要从销售记录文件中读取数据,然后处理并输出,每一本书的销售额,销售量和平均单价.我们假定对于每一个ISBN相同的记录都放在了一起.

程序将每一个ISBN相同的数据,相加到变量total中.使用第二个变量trans来保存从输入中读入的每一个交易.如果trans和total具有相同的ISBN,那么将两者相加,然后放在total中.否则,打印total,然后将才读到的数据重置total.

#include <iostream>
#include “Sales_item.h”

int main(){
    Sales_item total;//variable to hold data for the next transactions  
    //read the rirst transaction and ensure that ther are data to processing
    if (std::cin >> total){
        Sales_item trans;//variable to hold the running sum
        while(std::cin >> trans){
            //if we ‘re still processing the same book
            if(total.isbn() == trans.isbn()){
                total += trans;//update the running total
            }else{
                //print results for the previous book
                std::cout << total << std::endl;//print the last transaction
            }
        }
        std::cout <<  total << std::endl;//print the last transaction
    }else{
        //no input ! Warn the user
        std::cerr << “No data?!” << std::endl;
        return -1; //indicate failure
    }
    return 0;
}

这个程序是迄今为止,我们写的最复杂的程序.但是,它使用的都是我们已经熟悉的语句.

像往常一样,我们将要使用的头文件包含进来,我们使用了来自于标准库的iostream头文件以及我们自己的Sales_item.h头文件.在main函数里面我们定义了一个变量total,该变量将用来加上相同的ISBN号.一开始,我们将第一个数据读入total中,接着判断是否成功.如果没有任何记录可读,我们将跳转到else分支,else分支将会提示用户,没有输入.

假设我们已经成功的读取到了第一个记录,接下来执行if后面的代码块.这个代码块最开始定义了一个trans变量,这个变量用来保存我们读取到的数据.接下来执行while语句,while语句将读取剩下的记录.和以前的程序一样,while条件表达式从标准输入中读取值.在本例中,while读取一个Sales_item对象到trans变量中.只要读取成功就执行while的循环体

while循环体是一个if语句.if语句检查是否ISBN号相同.如果相同使用复合赋值运算符,将trans加到total中.如果ISBN不相同.打印total中存储的值,然后将total重置为才读取到的tans值.执行完if语句之后,回到while的条件表达式中,读取下一个记录.依次类推直到读完所有的记录.

当while语句执行完成.total包含有最后一个ISBN的数据.我们在语句块的最后,将这个数据输出.

小结(不译)
已经使用的术语(不译)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值