1-04 C++起步: 数据与代码的集成 —— C++对结构的扩充

来源:互联网 发布:化工仿真软件步骤 编辑:程序博客网 时间:2024/05/01 14:56

在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++面向对象程序设计的新工具 ——

原创粉丝点击