1.5 类的引入
解决书店问题前,剩下的唯一问题是弄明白如何编写数据结构来表示交易数据。C++(www.cppentry.com)中我们通过定义类来定义自己的数据结构。类机制是C++(www.cppentry.com)中最重要的特征之一。事实上,C++(www.cppentry.com)设计的主要焦点是使得定义的类类型的行为可以像内置类型一样自然。我们前面已看到的像istream和ostream这样的库类型,都定义为类——也就是说,它们严格说来不是语言的一部分。
完全理解类机制需要掌握很多内容。所幸我们可以使用他人写的类而无需掌握如何定义自己的类。在这一节,我们将描述一个用于解决书店问题的简单类。当我们学习了更多关于类型、表达式、语句和函数的知识(所有这些在类定义中都将用到)后,将会在后面的章节实现这个类。
使用类时我们需要知道三个问题:
(1) 类的名字是什么?
(2) 它在哪里定义?
(3) 它支持什么操作?
对于书店问题,我们假定类命名为Sales_item且类定义在命名为Sales_item.h的头文件中。
1.5.1 Sales_item类
Sales_item类的目的是存储ISBN并保存该书的销售册数、销售收入和平均售价。我们不关心如何存储或计算这些数据。使用类时我们不需要知道这个类是怎样实现的,相反,我们需要知道的是该类提供什么操作。
正如我们所看到的,使用像IO一样的库工具,必须包含相关的头文件。类似地,对于自定义的类,必须使得编译器可以访问和类相关的定义。这几乎可以采用同样的方式。一般来说,我们将类定义放入一个文件中,要使用该类的任何程序都必须包含这个文件。
依据惯例,类类型存储在一个文件中,其文件名如同程序的源文件名一样,由两部分组成:文件名和文件后缀。通常文件名和定义在头文件中的类名是一样的。通常后缀是.h,但也有一些程序员用.H、.hpp或.hxx。编译器通常并不挑剔头文件名,但IDE有时会。假设我们的类定义在名为Sale_item.h的文件中。
1. Sales_item对象上的操作
每个类定义一种类型,类型名与类名相同。因此,我们的Sales_item类定义了一种命名为Sales_item的类型。像使用内置类型一样,可以定义类类型的变量。当写下
< xml:namespace prefix = std />
就表示item是类型Sales_item的一个对象。通常将“类型Sales_item的一个对象”简称为“一个Sales_item对象”,或者更简单地简称为“一个Sales_item”。
除了可以定义Sales_item类型的变量,我们还可以执行Sales_item对象的以下操作:
使用加法操作符,+,将两个Sales_item相加
使用输入操作符,>>,来读取一个Sales_item对象
使用输出操作符,<<,来输出一个Sales_item对象
使用赋值操作符,=,将一个Sales_item对象赋值给另一个Sales_item对象
调用same_isbn函数确定两个Sales_item是否指同一本书。
2. 读入和写出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 99.96 24.99 |
输入表明销售了4本书,每本价格是24.99美元。输出表明卖出书的总数是4本,总收入是99.96美元,每本书的平均价格是24.99美元。
这个程序以两个#include指令开始,其中之一使用了一种新格式。iostream头文件由标准库定义,而Sales_item头文件则不是。Sales_item是一种自定义类型。当使用自定义头文件时,我们采用双引号(" ")把头文件名括起来。
标准库的头文件用尖括号< >括起来,非标准库的头文件用双引号" "括起来。
在main函数中,首先定义一个对象,命名为book,用它保存从标准输入读取的数据。下一条语句读入数据到此对象,第三条语句将它打印到标准输出,像平常一样紧接着打印endl来刷新缓冲区。
关键概念:类定义行为
在编写使用Sales_item的程序时,重要的是记住类Sales_item的创建者定义该类对象可以执行的所有操作。也就是说,Sales_item数据结构的创建者定义创建Sales_item对象时发生什么,以及加运算符或输入输出运算符应用到Sales_item对象时发生什么,等等。
通常,只有由类定义的操作可被用于该类类型的对象。此时,我们知道的可以在Sales_ item对象上执行的操作只是前面列出的那些。
我们将在7.7.3节和14.2节看到如何定义这些操作。
3. 将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 |
则输出为
程序首先包含两个头文件Sales_item和iostream。接下来定义两个Sales_item对象来存放要求和的两笔交易。输出表达式做加法运算并输出结果。从前面列出的操作,可以得知将两个Sales_item相加将创建一个新对象,新对象的ISBN是其操作数的ISBN,销售的数量和收入反映其操作数中相应值的和。我们也知道相加的项必须具有同样的ISBN。
值得注意的是这个程序是如何类似于1.2.2节中的程序:读入两个输入并输出它们的和。令人感兴趣的是,本例并不是读入两个整数并输出两个整数的和,而是读入两个Sales_item对象并输出两个Sales_item对象的和。此外,“和”的意义也不同。在整数的实例中我们产生的是传统求和——两个数值相加后的结果。在Sales_item对象的实例上我们使用了在概念上有新意义的求和——两个Sales_item对象的成分相加后的结果。
习题
习题1.21 网站(http://www.awprofessional.com/cpp_primer)的第1章的代码目录下有Sales_item.h的副本。复制该文件到你的工作目录。编写程序循环遍历一组书的销售交易,读入每笔交易并将交易写至标准输出。
习题1.22 编写程序读入两个具有相同ISBN的Sales_item对象并产生它们的和。
习题1.23 编写程序读入几个具有相同ISBN的交易,输出所有读入交易的和。
【责任编辑:
董书 TEL:(010)68476606】