1.5 类简介
一个类定义了一个类型,以及与其关联的一组操作。类机制是C++最重要的特性之一。实际上,C++最初的一个设计焦点就是能定义使用上像内置类型一样自然的类类型。
使用类,我们需要了解三件事:
- 类名是什么?
- 它是在哪里定义?
- 它支持什么操作?
我们假定类名为Sales_item,对于头文件Sales_item.h中已经定义了这个类。
如前所见,为了使用标准库设施,我们必须包含相关的头文件。我们也需要使用头文件来访问为自己的应用程序所定义的类。习惯上,头文件根据其中定义的类的名字来命名。我们通常使用.h作为头文件的后缀,但也有一些程序员习惯.H、.hpp或.hxx。标准库头文件通常不带后缀。编译器一般不关心头文件名的形式,但有的IDE对此有特定要求。
实际上,带.h的头文件和不带.h的头文件是两个不同的文件。使用带.h的头文件,相当于在C中调用库函数,使用的是全局命名空间,是早期的C++实现方法。后缀为.h的头文件在C++标准已经明确提出不再支持了,早些的C语言为了实现将标准库功能定义在全局空间里,声明放在带.h后缀的头文件里。C++标准为了和C语言分开,也为了正确使用命名空间,规定头文件不再使用后缀.h。
Sales_item.h:
#ifndef _CPP_SALES_ITEM_H
#define _CPP_SALES_ITEM_H
#include <string>
using namespace std;
class Sales_item {
public:
Sales_item();
Sales_item(string , int , double );
string isbn();
friend ostream& operator<<(ostream &output, Sales_item item);
friend istream& operator>>(istream &input, Sales_item &item);
Sales_item& operator=(const Sales_item&);
Sales_item operator+(const Sales_item&);
Sales_item& operator+=(const Sales_item&);
private:
string isbn_no;
int sales_cnt;
double sales_total;
};
#endif /* _CPP_SALES_ITEM_H */
Sales_item.cpp:
#include <iostream>
#include "Sales_item.h"
Sales_item::Sales_item() :
sales_cnt(0), sales_total(0) {}
Sales_item::Sales_item(string isbn, int sales_cnt, double sales_total)
{
isbn_no = isbn;
sales_cnt = sales_cnt;
sales_total = sales_total;
}
string Sales_item::isbn()
{
return isbn_no;
}
ostream& operator<<(ostream &output, Sales_item item)
{
output << item.isbn_no << " " << item.sales_cnt << " " << item.sales_total << " " << item.sales_total / item.sales_cnt;
return output;
}
istream& operator>>(istream &input, Sales_item &item)
{
double price;
input >> item.isbn_no >> item.sales_cnt >> price;
item.sales_total = price * item.sales_cnt;
return input;
}
Sales_item& Sales_item::operator=(const Sales_item &item)
{
isbn_no = item.isbn_no;
sales_cnt = item.sales_cnt;
sales_total = item.sales_total;
return *this;
}
Sales_item Sales_item::operator+(const Sales_item &item)
{
Sales_item sum = *this;
if(isbn_no == item.isbn_no) {
sum.sales_cnt += item.sales_cnt;
sum.sales_total += item.sales_total;
}
return sum;
}
Sales_item& Sales_item::operator+=(const Sales_item &item)
{
if(isbn_no == item.isbn_no) {
sales_cnt += item.sales_cnt;
sales_total += item.sales_total;
}
return *this;
}
1.5.1 Sales_item类
关键概念:类定义了行为
类的作者决定了类类型对象上可以使用的所有操作。
读写Sales_item
prog1.cpp:
#include <iostream>
#include "Sales_item.h"
using namespace std;
int main()
{
Sales_item book;
cin >> book;
cout << book << endl;
return 0;
}
input1:
0-201-70353-x 4 24.99
运行结果:
etc@ruc_etc:~/cpp/ch1/5$ g++ Sales_item.cpp prog1.cpp -o prog1 -Wall -g
etc@ruc_etc:~/cpp/ch1/5$ ./prog1 < input1
0-201-70353-x 4 99.96 24.99
Sales_item对象加法
prog2.cpp:
#include <iostream>
#include "Sales_item.h"
using namespace std;
int main()
{
Sales_item item1, item2;
cin >> item1 >> item2;
cout << item1 + item2 << endl;
return 0;
}
input2:
0-201-78345-x 3 20.00
0-201-78345-x 2 25.00
运行结果:
etc@ruc_etc:~/cpp/ch1/5$ g++ Sales_item.cpp prog2.cpp -o prog2 -Wall -g
etc@ruc_etc:~/cpp/ch1/5$ ./prog2 < input2
0-201-78345-x 5 110 22
此程序看起来与1.2节的程序非常相似。两个程序的不同之处是,“和”的概念是不一样的。对于Sales_item对象,我们用了一个全新的“和”的概念——两个Sales_item对象的成员对应相加的结果。
练习 1.20
略。
练习 1.21
exercise1_21.cpp:
#include <iostream>
#include "Sales_item.h"
using namespace std;
int main()
{
Sales_item item1, item2;
cin >> item1 >> item2;
cout << item1 + item2 << endl;
return 0;
}
input1_21:
0-201-70353-x 4 24.99
0-201-70353-x 3 19.99
运行结果:
etc@ruc_etc:~/cpp/ch1/5$ g++ Sales_item.cpp exercise1_21.cpp -o exercise1_21 -Wall -g
etc@ruc_etc:~/cpp/ch1/5$ ./exercise1_21 < input1_21
0-201-70353-x 7 159.93 22.8471
练习 1.22
exercise1_22.cpp:
#include <iostream>
#include <vector>
#include "Sales_item.h"
using namespace std;
int main()
{
int i, cnt;
Sales_item item;
vector<Sales_item > items;
for(i = 0, cin >> cnt; i < cnt; ++i) {
cin >> item;
items.push_back(item);
}
for(i = 0, item = items[i++]; i < cnt; ++i)
item += items[i];
cout << item << endl;
return 0;
}
input1_22:
4
0-201-70353-x 4 24.99
0-201-70353-x 3 19.99
0-201-70353-x 4 10.99
0-201-70353-x 3 39.99
运行结果:
etc@ruc_etc:~/cpp/ch1/5$ g++ Sales_item.cpp exercise1_22.cpp -o exercise1_22 -Wall -g
etc@ruc_etc:~/cpp/ch1/5$ ./exercise1_22 < input1_22
0-201-70353-x 14 323.86 23.1329
1.5.2 初识成员函数
prog3.cpp:
#include <iostream>
#include "Sales_item.h"
using namespace std;
int main()
{
Sales_item item1, item2;
cin >> item1 >> item2;
if(item1.isbn() == item2.isbn()) {
cout << item1 + item2 << endl;
return 0;
}
else {
cerr << "Data must refer to same ISBN" << endl;
return -1;
}
}
input3_1:
0-201-70353-x 4 24.99
0-201-70353-x 2 34.99
input3_2:
0-201-70353-x 4 24.99
1-201-70353-x 2 34.99
运行结果:
etc@ruc_etc:~/cpp/ch1/5$ g++ Sales_item.cpp prog3.cpp -o prog3 -Wall -g
etc@ruc_etc:~/cpp/ch1/5$ ./prog3 < input3_1
0-201-70353-x 6 169.94 28.3233
etc@ruc_etc:~/cpp/ch1/5$ ./prog3 < input3_2
Data must refer to same ISBN
成员函数是定义为类的一部分的函数,有时也被称为方法。
练习 1.23
exercise1_23.cpp:
#include <iostream>
#include <map>
#include "Sales_item.h"
using namespace std;
int main()
{
int i, cnt;
Sales_item item;
map<string, int > isbn_cnt;
for(i = 0, cin >> cnt; i < cnt; ++i) {
cin >> item;
++isbn_cnt[item.isbn()];
}
for(map<string, int >::iterator iter = isbn_cnt.begin(); iter != isbn_cnt.end(); ++iter)
cout << "[" << iter->first << "]: " << iter->second << endl;
return 0;
}
input1_23:
6
0-201-70353-x 4 24.99
2-201-70353-x 2 24.99
0-201-70353-x 3 19.99
1-201-70353-x 3 19.99
0-201-70353-x 4 10.99
1-201-70353-x 3 39.99
运行结果:
etc@ruc_etc:~/cpp/ch1/5$ g++ Sales_item.cpp exercise1_23.cpp -o exercise1_23 -Wall -g
etc@ruc_etc:~/cpp/ch1/5$ ./exercise1_23 < input1_23
[0-201-70353-x]: 3
[1-201-70353-x]: 2
[2-201-70353-x]: 1