在C语言里, 结构里只能包含数据成员, 例如下面的时钟类型:
struct TIME { int hour, minute, second; }; |
然后, 我们可以设计的一系列与之有关的函数。包括: 设置、输入、输出等, 使其用起来非常方便, 例如:
int main() { TIME a, b; // 定义时钟a和b Set(a, 12, 30, 15); // a设置成12:30:15 Output(a); // 输出a, 格式为"12:30:15" cout << "请输入时间: "; Input(b); // 输入b Output(b); // 输出b return 0; } |
首先编写输出函数, 它最简单:
void Output(const TIME &t) { cout << t.hour << ':' << t.minute << ':' << t.second << endl; } |
然后编写设置函数, 显然, 该函数应该对所给出的参数进行判断, 如果参数正确, 则进行设置操作, 否则, 就报告错误:
void Set(TIME &t, int h, int m, int s) { if (h>=0 && h<24 && m>=0 && m<60 && s>=0 && s<60) { t.hour = h; t.minute = m; t.second = s; } else cerr << "不正确的时钟数据: " << h << ", " << m << ", " << s << "/a/n"; } |
最后编写输入函数, 同样, 它也要判断用户从键盘输入的数据是否正确, 若用户输入的数据无效, 则要求重新输入:
void Input(TIME &t) { int h, m, s; while (cin >> h >> m >> s, h<0 || h>=24 || m<0 || m>=60 || s<0 || s>=60) { cout << "不正确的时钟数据: " << h << ", " << m << ", " << s << "/a/n请重新输入: "; } t.hour = h; t.minute = m; t.second = s; } |
等程序编到这里, 我们才发现, 对时钟数据的有效性判断是一个多次用到的功能, 所以它应该写成一个函数。这个函数的参数是时、分、秒, 函数值是逻辑值, 如果数据有效, 则函数返回真值, 否则为假值:
bool IsTime(int h, int m, int s) { if (h>=0 && h<24 && m>=0 && m<60 && s>=0 && s<60) return true; else return false; } |
初学者的经常写出这样的代码, 这说明他对关系运算和逻辑类型的理解尚不透彻。下面的推荐的写法:
bool IsTime(int h, int m, int s) { return h>=0 && h<24 && m>=0 && m<60 && s>=0 && s<60; } |
这里, 关系表达式(h>=0 && h<24 && m>=0 && m<60 && s>=0 && s<60)的值是一个逻辑值, 不是true, 就是false, 根本不需要写什么if语句。
有了这个判断时钟数据有效性的函数, 我们可以改写设置函数和输入函数, 使其更简洁、明了:
void Set(TIME &t, int h, int m, int s) { if (IsTime(h, m, s)) { t.hour = h; t.minute = m; t.second = s; } else cerr << "不正确的时钟数据/n"; } |
若 h, m, s 是有效的时钟数据, 则设置时钟; 否则显示错误信息。
void Input(TIME &t) { int h, m, s; while (cin >> h >> m >> s, !IsTime(h, m, s)) { cout << "不正确的时钟数据/n请重新输入: "; } t.hour = h; t.minute = m; t.second = s; } |
若用户输入的 h, m, s 不是有效的时钟数据, 则通过循环, 要求用户重新输入; 若数据有效则结束循环, 然后设置时钟。
下面是完整的程序:
TIME.H
#ifndef _TIME_H_ #define _TIME_H_ struct TIME { int hour, minute, second; }; bool IsTime(int h, int m, int s); void Set(TIME &t, int h, int m, int s); void Input(TIME &t); void Output(const TIME &t); #endif |
TIME.CPP
#include <iostream> using namespace std; #include "time.h" bool IsTime(int h, int m, int s) { return h>=0 && h<24 && m>=0 && m<60 && s>=0 && s<60; } void Set(TIME &t, int h, int m, int s) { if (IsTime(h, m, s)) { t.hour = h; t.minute = m; t.second = s; } else cerr << "不正确的时钟数据: " << h << ", " << m << ", " << s << "/a/n"; } void Input(TIME &t) { int h, m, s; while (cin >> h >> m >> s, !IsTime(h, m, s)) { cout << "不正确的时钟数据: " << h << ", " << m << ", " << s << "/a/n请重新输入: "; } t.hour = h; t.minute = m; t.second = s; } void Output(const TIME &t) { cout << t.hour << ':' << t.minute << ':' << t.second << endl; } |
MAIN.CPP
#include <iostream> using namespace std; #include "time.h" int main() { TIME a, b; Set(a, 12, 30, 15); // 数据正确 Output(a); Set(a, 200, -45, 95); // 数据错误 Output(a); cout << "请输入时间: "; Input(b); Output(b); return 0; } |
程序运行结果如下, 其中用户第一次输入的数据是: 25时30分88秒, 第二次重新输入的数据是: 23时59分58秒:
12:30:15 不正确的时钟数据: 200, -45, 95 12:30:15 请输入时间: 25 30 88 不正确的时钟数据: 25, 30, 88 请重新输入: 23 59 58 23:59:58 |
在上面的例子以及1-03的例子里面, 在结构中只定义了数据成员, 而函数都是独立于结构之外的全局函数, 即从语言的角度来看, 函数与结构并无内在联系; 然而, 从问题本身来看, 显然这一组函数与结构有着密切的联系, 它们本应该是一个不可分割的整体, 只是因为C语言的表达能力有限, 使它们看起来互不相干。
C++对C的结构进行了扩充, 允许在结构里定义数据成员和成员函数, 实现了数据和函数的初步集成, 它们形成了一个完整的整体:
struct TIME { int hour, minute, second; bool IsTime(int h, int m, int s); void Set(int h, int m, int s); void Input(); void Output(); }; |
成员函数与普通函数不同, 它们的使用的方法发生了变化:
int main() { TIME a, b; a.Set(12, 30, 15); // 对a发消息, 令其变为12:30:15 a.Output(); // 对a发消息, 令其输出内容 cout << "请输入时间: "; b.Input(); // 对b发消息, 令其从键盘输入新内容 b.Output(); // 对b发消息, 令其输出内容 return 0; } |
这正是面向对象程序的特点, 程序是由对象和向对象发出的消息组成。即:
程序 = 对象 + 消息
面向过程的程序 (普通函数) | 面向对象的程序 (成员函数) |
Set(a, 12, 30, 15); Input(a); Output(a); | a.Set(12, 30, 15); a.Input(); a.Output(); |
在面向过程的程序里, 以功能为核心。语句是Input(a), 即:
- 您要做什么? - 输入数据! - 保存到哪里? - 变量a中! |
在面向对象的程序里, 以对象为核心。语句是a.Input(), 即:
- 您要找谁? - 找对象a! - 要它做什么? - 输入数据! |
用录音机做比喻。在面向过程的程序里, 我们首先要决定做什么操作, 如录音、放音、快进或快退等, 然后再看把操作施回到哪一台录音机上; 而在面向对象的程序里, 录音机是一个对象, 我们首先确定用哪一台录音机, 然后向该录音机发出操作指令, 录音机似乎是一台智能设备, 能自动响应我们的要求。
定义在结构中的函数称为成员函数, 为了与普通函数以示区别, 定义它们时, 成员函数名前要冠以类的名称, 并以作用域运算符“::”隔开。例如:
bool TIME::IsTime(int h, int m, int s) { return h>=0 && h<24 && m>=0 && m<60 && s>=0 && s<60; } |
用下面的方法写出的函数, 不是成员函数, 它不属于任何类, 是一个“独立”的函数:
bool IsTime(int h, int m, int s) { return h>=0 && h<24 && m>=0 && m<60 && s>=0 && s<60; } |
下面是完整的程序:
TIME.H
#ifndef _TIME_H_ #define _TIME_H_ struct TIME { int hour, minute, second; bool IsTime(int h, int m, int s); void Set(int h, int m, int s); void Input(); void Output() const; }; #endif |
TIME.CPP
#include <iostream> using namespace std; #include "time.h" bool TIME::IsTime(int h, int m, int s) { return h>=0 && h<24 && m>=0 && m<60 && s>=0 && s<60; } void TIME::Set(int h, int m, int s) { if (IsTime(h, m, s)) { hour = h; minute = m; second = s; } else cerr << "不正确的时钟数据: " << h << ", " << m << ", " << s << "/a/n"; } void TIME::Input() { int h, m, s; while (cin >> h >> m >> s, !IsTime(h, m, s)) { cout << "不正确的时钟数据: " << h << ", " << m << ", " << s << "/a/n请重新输入: "; } hour = h; minute = m; second = s; } void TIME::Output() const { cout << hour << ':' << minute << ':' << second << endl; } |
MAIN.CPP
#include <iostream> using namespace std; #include "time.h" int main() { TIME a, b; a.Set(12, 30, 15); // 数据正确 a.Output(); a.Set(200, -45, 95); // 数据错误 a.Output(); cout << "请输入时间: "; b.Input(); b.Output(); return 0; } |
程序运行结果如下, 其中用户第一次输入的数据是: 25时30分88秒, 第二次重新输入的数据是: 23时59分58秒:
12:30:15 不正确的时钟数据: 200, -45, 95 12:30:15 请输入时间: 25 30 88 不正确的时钟数据: 25, 30, 88 请重新输入: 23 59 58 23:59:58 |
这个版本已经与C++的类非常接近了, 只是结构中的成员默认的访问属性公有的。公有的成员, 在类任何地方都可以访问, 因此, 用户以下面的几种方式来访问结构, 都是合法的:
通过成员函数访问 | 直接操纵数据成员 |
a.Set(12, 30, 15); | a.hour = 12; a.minute = 30; a.second = 15; |
a.Input(); | cin >> a.hour >> a.minute >> a.second; |
a.Output(); | cout << a.hour << ':' << a.minute << ':' << a.second << endl; |
显然, 结构虽然提供了将数据成员和成员函数集成到一起的手段, 但所有成员都是公有的, 所以它不安全。用户设置时钟, 如果通过成员函数来设置, 由于设置函数具有判断功能, 不会允许错误的数据; 如果用户不通过成员函数设置, 直接操纵数据成员, 那么这个程序就无能为力了, 比如用户可以作以下错误设置:
a.hour = 200; a.minute = -45; a.second = 95;
要解决这个问题, 需要使用C++面向对象程序设计的新工具 —— 类。