《C++ Primer 5th》笔记(1 / 19):C++基础 爱被打了一巴掌 2023-01-14 15:56 163阅读 0赞 ### 文章目录 ### * * 编写一个简单的C++程序 * * 编译、运行程序 * 初识输入输出 * 注释简介 * 控制流 * * while语句 * for语句 * 读取数量不定的输入数据 * if语句 * 类简介 * * Sales\_item类 * 初识成员函数 * 书店程序 * 一些术语 ## 编写一个简单的C++程序 ## 每个C++程序都包含一个或多个函数(function),其中一个**必须**命名为main。操作系统通过调用main来运行C++程序。 int main() { return 0; } 一个函数的定义包含四部分: 1. **返回类型**(return type)、 2. **函数名**(function name)、 3. 一个括号包围的**形参列表**(parameter list,允许为空) 4. **函数体**(function body)。 虽然main函数在某种程度上比较特殊,但其定义与其他函数是一样的。main函数的返回类型必须为 int,即整数类型。int类型是一种**内置类型**(built-intype),即语言自身定义的类型。 函数定义的最后一部分是函数体,它是一个以左花括号(curly brace)开始,以右花括号结束的**语句块**(block of statements)。 这个语句块中唯一的一条语句是 return,它结束函数的执行。在本例中,return还会向调用者返回一个值。当return 语句包括一个值时,此返回值的类型必须与函数的返回类型相容。在本例中,main的返回类型是int,而返回值0的确是一个int类型的值。 注意,语句以;号结束,不能省略。 在大多数系统中,main的返回值被用来指示状态。返回值0表明成功,非0的返回值的含义由系统定义,通常用来指出错误类型。 **重要概念:类型** 一种类型(Type)不仅定义了数据元素的内容,还定义了这类数据上可以进行的运算。 程序所处理的数据都保存在变量中,而每个变量都有自己的类型。如果一个名为v的变量的类型为T,我们通常说“v是一个T类型变量”。 ### 编译、运行程序 ### 编写好程序后,要编译它。如何编译程序依赖于你使用的操作系统和编译器。 很多PC机上的编译器都具备**集成开发环境**(Integrated Developed Environment,IDE),将编译器与其他程序创建和分析工具包装在一起。在开发大型程序时,这类集成环境是非常有用的工具。 大部分编译器,包括集成IDE的编译器,都会提供一个命令行界面。除非你已经了解IDE,否则你会觉得借助命令行界面开始学习C++还是很容易的。这种学习方式的好处是,可以先将精力集中于C++语言本身(而不是一些开发工具),而且,一旦你掌握了语言,IDE通常是很容易学习的。(**先学语言,后学IDE**) **程序源文件命名约定** 无论你使用命令行界面或者IDE,大多数编译器都要求程序源码存储在一个或多个文件中。程序文件通常被称为**源文件**(source file)。在大多数系统中,源文件的名字以一个后缀为结尾。后缀告诉系统这个文件是一个C++程序。常见的后缀命名有.cpp、.c。 **从命令行运行编译器** 如Linux版的: gcc prog1.cc g++ -o prog1 prog1.cc ## 初识输入输出 ## C++语言并未定义任何输入输出(IO)语句,取而代之,包含了一个全面的**标准库**(standard library)来提供IO机制(以及很多其他设施)。 接下来很多示例都使用了iostream库。 iostream库包含两个**基础类型**: 1. istream 输入流 2. ostream 输出流 **一个流就是一个字符序列,是从IO设备读出或写入IO设备的**。 术语“流”(stream)想要表达的是,随着时间的推移,字符是顺序生成或消耗的。 **标准输入输出对象** 标准库定义了4个IO对象: 1. cin(发音为see-in):istream类型的对象,也被称为标准输入(standard input),为了处理输入。 2. cout(发音为see-out):ostream类型的对象,也被称为标准输出(standard output),为了处理输出。 3. cerr(发音为see-err):ostream类型的对象,也被称为标准错误(standard error),用来输出警告和错误消息。 4. clog(发音为see-log):ostream类型的对象,用来输出程序运行时的一般性信息。 系统通常将程序所运行的窗口与这些对象关联起来。因此, * 当我们读取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: 1 2 The sum of 1 and 2 is 3 Process returned 0 (0x0) execution time : 3.566 s Press any key to continue. 接下来是上一源程序的详细解释: 程序的第一行 #include <iostream> 告诉编译器我们想要使用iostream库。尖括号中的名字(本例中是 iostream)指出了一个头文件(header)。每个使用标准库设施的程序都必须包含相关的头文件。 \#include指令和头文件的名字必须写在同一行中。通常情况下,\#include 指令必须出现在所有函数之外。我们**一般将一个程序的所有\#include指令都放在源文件的开始位置**。 **一、向流写入数据** main 的函数体的第一条语句执行了一个**表达式**(expression)。在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; 1. 输出运算符给用户打印一条消息。这个消息是一个**字符串字面值常量**(string literal),是用一对双引号包围的字符序列。在双引号之间的文本被打印到标准输出。 2. 运算符打印 endl,这是一个被称为**操纵符**(manipulator)的特殊值。\*\*写入endl的效果是结束当前行,并将与设备关联的缓冲区(buffer)中的内容刷到设备中。\*\*缓冲刷新操作可以保证到目前为止程序所产生的所有输出都真正写入输出流中,而不是仅停留在内存中等待写入流。 **二、使用标准库中的名字** 这个程序使用了std::cout和std::endl,而不是直接的cout和endl。 前缀std::指出名字cout和 endl是定义在名为std的**命名空间**(namespace)中的。\*\*命名空间可以帮助我们避免不经意的名字定义冲突,以及使用库中相同名字导致的冲突。\*\*标准库定义的所有名字都在命名空间std中。(这好似我们谈到黄埔区,是指上海市的黄埔区,还是广州市的呢?为了避免误解,需要在黄埔区这一词前加上海市或广州市来区别)。 通过命名空间使用标准库有一个副作用:当使用标准库中的一个名字时,必须显式说明我们想使用来自命名空间std中的名字。例如,需要写出std: :cout,通过使用**作用域运算符**(::)来指出我们想使用定义在命名空间std中的名字cout。 如果你一个程序,只使用std这单一**命名空间**,重复std::会让程序显得不那么优雅,将来会回给出一个更简单的访问标准库中名字的方法。(拭目以待) **三、从流读取数据** 在提示用户输入数据之后,接下来希望读入用户的输入。 首先定义两个名为 v1和v2的变量(variable)来保存输入: int v1 = 0,v2 = 0; 我们将这两个变量定义为int类型,int是一种内置类型,用来表示整数。还将它们**初始化**(initialize)为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 " << vl + v2 << std::endl; 这条语句将每个运算对象打印在标准输出上。(虽然有点长) 本例一个有意思的地方在于,运算对象并不都是相同类型的值。某些运算对象是**字符串字面值常量**,例如"The sum of "。其他运算对象则是int值,如v1、v2以及算术表达式v1+v2的计算结果。**标准库定义了不同版本的输入输出运算符,来处理这些不同类型的运算对象。**(类比Java的对象toString())。 ## 注释简介 ## 注释可以帮助阅码者理解程序。注释通常用于概述算法,确定变量的用途,或者解释晦涩难懂的代码段。编译器会忽略注释,因此注释对程序的行为或性能不会有任何影响。 虽然编译器会忽略注释,但读者并不会。即使系统文档的其他部分已经过时,程序员也倾向于相信注释的内容是正确可信的。因此,错误的注释比完全没有注释更糟糕,因为它会误导阅码者。因此,当你修改代码时,切记注释也要修改。 C++有两种注释: 1. 单行注释 // 2. 多行注释 /\* \*/(界定符对注释),**这个不能嵌套**。 注释另有一个用法:你调试代码时,一段代码暂时不能移除且又不想主程序执行它,此时你可以注释掉这一段代码。 ## 控制流 ## 程序控制流主要有三种: 1. 顺序 2. 条件 3. 循环 ### while语句 ### while语句反复执行一段代码,直至给定条件为假为止。 用while语句编写一段程序,求1到10这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 Process returned 0 (0x0) execution time : 0.012 s Press any key to continue. while语句的形式为: while (condition) statement while 语句的执行过程是交替地检测condition条件和执行关联的语句statement,直至condition为假时停止。所谓**条件**(condition)就是一个产生真或假的结果的表达式。只要condition为真,statement 就会被执行。当执行完statement,会再次检测condition。如果condition仍为真,statement再次被执行。while 语句持续地交替检测condition和执行statement,直至condition为假为止。 上面程序相关专有名词 1. 语句块(block),就是用花括号包围的零条或多条语句的序列。 2. 复合赋值运算符(+=) 3. 赋值(assignment)(=) 4. 前缀递增运算符(++) ### for语句 ### 在我们的while循环例子中,使用了变量val来控制循环执行次数。我们在循环条件中检测val的值,在 while循环体中将val递增。 这种在**循环条件中检测变量**、在**循环体中递增变量**的模式使用非常频繁,以至于C++语言专门定义了第二种循环语句—**for语句**,来简化符合这种模式的语句。 可以用for语句来重写从1加到10的程序: #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; } 每个 for语句都包含两部分:循环头和循环体。 循环头控制循环体的执行次数,它由三部分组成: 1. 一个初始化语句(init-statement)、 2. 一个循环条件(condition)、 3. 一个表达式(expression)。 ### 读取数量不定的输入数据 ### #include <iostream> int main() { int sum = 0, value = 0; // read until end-of-file, calculating a running total of all values read while (std::cin >> value) sum += value; // equivalent to sum = sum + value std::cout << "Sum is: " << sum << std::endl; return 0; } 输出结果: 3 4 5 6 7 8 9 10^Z Sum is: 52 Process returned 0 (0x0) execution time : 16.531 s Press any key to continue. 当我们使用一个istream对象作为条件时,其效果是检测流的状态。如果流是有效的,即流未遇到错误,那么检测成功。当遇到**文件结束符**(end-of-file),或遇到一个无效输入时(例如读入的值不是一个整数),istream对象的状态会变为无效。处于无效状态的istream对象会使条件变为假。 **从键盘输入文件结束符**: 1. Windows:Ctrl+Z,再Enter 2. Unix/Linux:Ctrl+D **再谈编译**: 编译器的一部分工作是寻找程序文本中的错误。编译器没有能力检查一个程序是否按照其作者的意图工作,但可以检查形式(form)上的错误。下面列出了一些最常见的编译器可以检查出的错误: 1. 语法错误(syntax error),如字符串字面常量的两侧漏掉了引号,return语句漏掉了分号。 2. 类型错误(type error),如向一个期望参数为int的函数传递了一个字符串字面值常量(张冠李戴)。 3. 声明错误(declaration error),如对来自标准库的名字忘记使用std: :、标识符名字拼写错误。 错误信息通常包含一个行号和一条简短描述,描述了编译器认为的我们所犯的错误。 **两个好习惯:** 1. 按照报告的顺序来逐个修正错误,是一种好习惯。因为一个单个错误常常会具有传递效应,导致编译器在其后报告比实际数量多得多的错误信息。 2. 另一个好习惯是在每修正一个错误后就立即重新编译代码,或者最多是修正了一小部分明显的错误后就重新编译。这就是所谓的“编辑-编译-调试"(edit-compile-debug)周期。 ### if语句 ### #include <iostream> int main() { // currVal is the number we're counting; we'll read new values into val int currVal = 0, val = 0; // read first number and ensure that we have data to process 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; // add 1 to 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; // reset the counter } } // 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; } 输出结果: 41 41 41 41 41 14 41 14^Z 41 occurs 5 times 14 occurs 1 times 41 occurs 1 times 14 occurs 1 times Process returned 0 (0x0) execution time : 19.803 s Press any key to continue. 注意,相等运算符==与赋值运算符=的区别。 **关键概念:C++程序的缩进和格式** C++程序很大程度上是格式自由的,也就是说,我们在哪里放置花括号、缩进、注释以及换行符通常不会影响程序的语义。 虽然很大程度上可以按照自己的意愿自由地设定程序的格式,但我们所做的选择会影响程序的可读性。例如,我们可以将整个main函数写在很长的单行内,虽然这样是合乎语法的,但会非常难读。 关于C/C++的正确格式的辩论是无休止的。我们的信条是,不存在唯一正确的风格,但保持一致性是非常重要的。 **我们要牢记一件重要的事情**:其他可能的程序格式总是存在的,当你要选择一种格式风格时,思考一下它会对程序的可读性和易理解性有什么影响,而一旦选择了一种风格,就要坚持使用。 ## 类简介 ## 在C++中,我们**通过定义一个类(class)来定义自己的数据结构(data structure)**。一个类定义了一个类型,以及与其关联的一组操作。类机制是C++最重要的特性之一。 实际上,C++最初的一个设计焦点就是能定义使用上像**内置类型**一样自然的**类类型**(class type)。 为了使用标准库设施,我们必须包含相关的头文件。类似的,**我们也需要使用头文件来访问为自己的应用程序所定义的类**。 习惯上,头文件根据其中定义的类的名字来命名。我们通常使用.h作为头文件的后缀,但也有一些程序员习惯.H、.hpp 或.hxx。标准库头文件通常不带后缀。编译器一般不关心头文件名的形式,但有的IDE对此有特定要求。 Sales\_item.h一览: /* This file defines the Sales_item class used in chapter 1. * The code used in this file will be explained in * Chapter 7 (Classes) and Chapter 14 (Overloaded Operators) * Readers shouldn't try to understand the code in this file * until they have read those chapters. */ #ifndef SALESITEM_H // we're here only if SALESITEM_H has not yet been defined #define SALESITEM_H // Definition of Sales_item class and related functions goes here #include <iostream> #include <string> class Sales_item { // these declarations are explained section 7.2.1, p. 270 // and in chapter 14, pages 557, 558, 561 friend std::istream& operator>>(std::istream&, Sales_item&); friend std::ostream& operator<<(std::ostream&, const Sales_item&); friend bool operator<(const Sales_item&, const Sales_item&); friend bool operator==(const Sales_item&, const Sales_item&); public: // constructors are explained in section 7.1.4, pages 262 - 265 // default constructor needed to initialize members of built-in type Sales_item() = default; Sales_item(const std::string &book): bookNo(book) { } Sales_item(std::istream &is) { is >> *this; } public: // operations on Sales_item objects // member binary operator: left-hand operand bound to implicit this pointer Sales_item& operator+=(const Sales_item&); // operations on Sales_item objects std::string isbn() const { return bookNo; } double avg_price() const; // private members as before private: std::string bookNo; // implicitly initialized to the empty string unsigned units_sold = 0; // explicitly initialized double revenue = 0.0; }; // used in chapter 10 inline bool compareIsbn(const Sales_item &lhs, const Sales_item &rhs) { return lhs.isbn() == rhs.isbn(); } // nonmember binary operator: must declare a parameter for each operand Sales_item operator+(const Sales_item&, const Sales_item&); inline bool operator==(const Sales_item &lhs, const Sales_item &rhs) { // must be made a friend of Sales_item return lhs.units_sold == rhs.units_sold && lhs.revenue == rhs.revenue && lhs.isbn() == rhs.isbn(); } inline bool operator!=(const Sales_item &lhs, const Sales_item &rhs) { return !(lhs == rhs); // != defined in terms of operator== } // assumes that both objects refer to the same ISBN Sales_item& Sales_item::operator+=(const Sales_item& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } // assumes that both objects refer to the same ISBN Sales_item operator+(const Sales_item& lhs, const Sales_item& rhs) { Sales_item ret(lhs); // copy (|lhs|) into a local object that we'll return ret += rhs; // add in the contents of (|rhs|) return ret; // return (|ret|) by value } std::istream& operator>>(std::istream& in, Sales_item& s) { double price; in >> s.bookNo >> s.units_sold >> price; // check that the inputs succeeded if (in) s.revenue = s.units_sold * price; else s = Sales_item(); // input failed: reset object to default state return in; } std::ostream& operator<<(std::ostream& out, const Sales_item& s) { out << s.isbn() << " " << s.units_sold << " " << s.revenue << " " << s.avg_price(); return out; } double Sales_item::avg_price() const { if (units_sold) return revenue/units_sold; else return 0; } #endif ### Sales\_item类 ### 类的作用是表示一本书的总销售额、售出册数和平均售价。 现在可以不关心这些数据如何存储、如何计算。为了使用一个类,我们不必关心它是如何实现的,只需知道类对象可以执行什么操作。 每个类实际上都定义了一个新的类型,其类型名就是类名。因此,我们的sales\_item类定义了一个名为sales\_item的类型。与内置类型一样,我们可以定义类类型的变量。当我们写下如下语句 Sales_item sales_item; 是想表达item是一个sales\_item类型的对象。我们通常将“一个sales\_item类型的对象”简单说成“一个sales\_item对象”,或更简单的“一个sales\_item”。 除了可以定义sales\_item类型的变量之外,我们还可以: * 调用一个名为isbn的函数从一个sales\_item对象中提取工ISBN书号。 * 用输入运算符(>>)和输出运算符(<<)读、写sales\_item类型的对象。 * 用赋值运算符(=)将一个sales\_item对象的值赋予另一个sales\_item对象。 * 用加法运算符(+)将两个sales\_item对象相加。两个对象必须表示同一本书(相同的ISBN)。加法结果是一个新的sales\_item对象,其ISBN与两个运算对象相同,而其总销售额和售出册数则是两个运算对象的对应值之和。 * 使用复合赋值运算符(+=)将一个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^Z 0-201-70353-X 4 99.96 24.99 Process returned 0 (0x0) execution time : 28.364 s Press any key to continue. 这里涉及运算符重载,重点回看头文件Sales\_item.h: std::istream& operator>>(std::istream& in, Sales_item& s) { double price; in >> s.bookNo >> s.units_sold >> price; // check that the inputs succeeded if (in) s.revenue = s.units_sold * price; else s = Sales_item(); // input failed: reset object to default state return in; } std::ostream& operator<<(std::ostream& out, const Sales_item& s) { out << s.isbn() << " " << s.units_sold << " " << s.revenue << " " << s.avg_price(); return out; } **二、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 Process returned 0 (0x0) execution time : 70.536 s Press any key to continue. 这里也运算符重载,重点回看头文件Sales\_item.h: // assumes that both objects refer to the same ISBN Sales_item& Sales_item::operator+=(const Sales_item& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } // assumes that both objects refer to the same ISBN Sales_item operator+(const Sales_item& lhs, const Sales_item& rhs) { Sales_item ret(lhs); // copy (|lhs|) into a local object that we'll return ret += rhs; // add in the contents of (|rhs|) return ret; // return (|ret|) by value } **使用文件重定向** 当你测试程序时,反复从键盘敲入这些销售记录作为程序的输入,是非常乏味的。大多数操作系统支持文件重定向,这种机制允许我们将标准输入和标准输出与命名文件关联起来: $ addItems <infile >outfile 假定$是操作系统提示符,我们的加法程序已经编译为名为addItems.exe的可执行文件(在 UNIX中是addItems),则上述命令会从一个名为infile的文件读取销售记录,并将输出结果写入到一个名为outfile的文件中,两个文件都位于当前目录中。 ### 初识成员函数 ### #include <iostream> #include "Sales_item.h" int main() { Sales_item item1, item2; std::cin >> item1 >> item2; // first check that item1 and item2 represent 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 } } **什么是成员函数?** 上一程序中的item1.isbn() 调用名为isbn 的**成员函数**(member function)。成员函数是定义为类的一部分的函数,有时也被称为**方法**(method)。 我们通常以一个类对象的名义来调用成员函数。例如,上面相等表达式左侧运算对象的第一部分 item1.isbn()。 使用**点运算符**(.)来表达我们需要“名为item1的对象的isbn成员”。点运算符只能用于类类型的对象。其左侧运算对象必须是一个类类型的对象,右侧运算对象必须是该类型的一个成员名,运算结果为右侧运算对象指定的成员。 当用点运算符访问一个成员函数时,通常我们是想(效果也确实是)调用该函数。我们使用**调用运算符**(())来调用一个函数。调用运算符是一对圆括号,里面放置实参( argument)列表(可能为空)。成员函数isbn并不接受参数。因此 iteml.isbn() 调用名为item1的对象的成员函数isbn,此函数返回item1中保存的ISBN书号。 NOTE: .不是调用运算符,而是()。 ## 书店程序 ## 现在我们已经准备好完成书店程序了。我们需要从一个文件中读取销售记录,生成每本书的销售报告,显示售出册数、总销售额和平均售价。我们假定每个ISBN书号的所有销售记录在文件中是聚在一起保存的。 我们的程序会将每个ISBN的所有数据合并起来,存入名为 total的变量中。我们使用另一个名为trans 的变量保存读取的每条销售记录。如果trans和total指向相同的ISBN,我们会更新total的值。否则,我们会打印 total的值,并将其重置为刚刚读取的数据(trans): #include <iostream> #include "Sales_item.h" int main() { Sales_item total; // variable to hold data for the next transaction // read the first transaction and ensure that there are data to process if (std::cin >> total) { Sales_item trans; // variable to hold the running sum // read and process the remaining transactions 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; total = trans; // total now refers to the next book } } 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; } ## 一些术语 ## **缓冲区**(buffer)一个存储区域,用于保存数据。IO设施通常将输入(或输出)数据保存在一个缓冲区中,读写缓冲区的动作与程序中的动作是无关的(read or write the buffer independently from actions in the program)(Note:不是buffer.read()或buffer.write(),而是cin>>xx,cout <<xx,我是这样理解的,不知道对吗?)。我们可以显式地刷新输出缓冲,以便强制将缓冲区中的数据写入输出设备。默认情况下,读cin会刷新cout;程序非正常终止时也会刷新cout。 **类**(class)一种用于定义**自己的**数据结构及其相关操作的机制。类是C++中最基本的特性之一。标准库类型中,如istream和 ostream都是类。(Facility for defining our **own data structures** together with **associated operations**. The class is one of the most fundamental features in C+ + . Library types, such as istream and ostream, are classes. ) **类型**(Type)不仅定义了数据元素的内容,还定义了这类数据上可以进行的运算。(A type defines both the contents of a data element and the operations that are possible on those data. )程序所处理的数据都保存在变量中,而每个变量都有自己的类型。(The data our programs manipulate are stored in variables and every variable has a type. )如果一个名为v的变量的类型为T,我们通常说“v是一个T类型变量”。(When the type of a variable named v is T, we often say that “v is a T.” ) **类类型**(class type)类定义的类型。类名即为类型名。(A type defined by a class. The name of the type is the class name. )(Note:个人理解class和type是一对孪生子。) **花括号**(curly brace)。 **数据结构**(data structure)数据及其上所允许的操作的一种逻辑组合。(A logical grouping of data and operations on that data. ) **初始化**(initialize)在一个对象创建的时候赋予它一个值。 **未初始化的变量**(uninitialized variable)未赋予初值的变量。类类型的变量如果未指定初值,则按类定义指定的方式进行初始化。定义在函数内部的内置类型变量默认是不初始化的,除非有显式的初始化语句。试图使用一个未初始化变量的值是错误的。未初始化变量是bug 的常见成因。 **操纵符**(manipulator)对象,如std::endl,在读写流的时候用来“操纵”流本身。
相关 《C++ Primer 5th》笔记(10 / 19):泛型算法 文章目录 概述 算法如何工作 迭代器令算法不依赖于容器 但算法依赖于元素类型的操作 我会带着你远行/ 2023年01月22日 08:53/ 0 赞/ 14 阅读
相关 《C++ Primer 5th》笔记(9 / 19):顺序容器 文章目录 顺序容器概述 确定使用哪种顺序容器 容器库概览 迭代器 迭代器范围 淩亂°似流年/ 2023年01月21日 02:21/ 0 赞/ 13 阅读
相关 《C++ Primer 5th》笔记(8 / 19):IO库 文章目录 IO类 IO类型间的关系 IO对象无拷贝或赋值 条件状态 素颜马尾好姑娘i/ 2023年01月19日 08:19/ 0 赞/ 19 阅读
相关 《C++ Primer 5th》笔记(7 / 19):类 文章目录 定义抽象数据类型 设计Sales\_data类 关键概念:不同的编程角色 缺乏、安全感/ 2023年01月19日 04:47/ 0 赞/ 19 阅读
相关 《C++ Primer 5th》笔记(5 / 19):语句 文章目录 简单语句 空语句 别漏写分号,也别多写分号 复合语句(块) 语句作用域 墨蓝/ 2023年01月18日 04:28/ 0 赞/ 127 阅读
相关 《C++ Primer 5th》笔记(4 / 19):表达式 文章目录 基础 基本概念 组合运算符和运算对象 运算对象转换 缺乏、安全感/ 2023年01月17日 15:00/ 0 赞/ 156 阅读
相关 《C++ Primer 5th》笔记(1 / 19):C++基础 文章目录 编写一个简单的C++程序 编译、运行程序 初识输入输出 注释简介 控制流 爱被打了一巴掌/ 2023年01月14日 15:56/ 0 赞/ 164 阅读
相关 《C++ Primer 5th》笔记(12 / 19):动态内存 文章目录 动态内存与智能指针 shared\_ptr类 make\_shared函数 痛定思痛。/ 2022年10月16日 07:33/ 0 赞/ 215 阅读
相关 《C++ Primer 5th》笔记(11 / 19):关联容器 文章目录 使用关联容器 使用map 使用set 关联容器概述 定义关联容器 一时失言乱红尘/ 2022年10月14日 05:41/ 0 赞/ 357 阅读
还没有评论,来说两句吧...