当前位置: 首页 > 文档资料 > C++大学教程 >

6.7 接口与实现方法的分离

优质
小牛编辑
133浏览
2023-12-01

良好软件工程的一个基本原则是将接口与实现方法分离,这样可以更容易修改程序。就类的客户而言,类实现方法的改变并不影响客户,只要类的接口保持不变即可(类的功能可能扩展到原接口以外)。

软件工程视点 6.10
将类声明放在使用该类的任何客户的头文件中,这就形成类的Public接口(并向客户提供调用类成员函数所需的函数原型)。将类成员函数的定义放在源文件中,这就形成类的实现方法。

软件工程视点 6.11
类的客户使用类时不需要访问类的源代码,但客户需要连挂类的目标码。这样就可以由独立软件供应商(ISV)提供类库进行销售和发放许可证。ISV只在产品中提供头文件和目标模块,不提供专属信息(例如源代码)。C++ 用户可以享用更多的ISV生产的类库。

实际上,任何事情都不是十全十美的。头文件中包含一些实现部分,并隐藏了实现方法的其他函数定义。private成员列在头文件的类定义中.因此客户虽然无法访问private成员,但能看到这些成员。第7章将介绍如何用代理类从类的客户中隐藏类的private数据。

软件工程视点 6.12
对类接口很重要的信息应放在头文件中。只在类内部使用而类的客户不需要的信息应放在不发表的源文件中。这是最低权限原则的又一个例子。

图 6.5 将图 6.3 的程序分解为多个文件。建立C++程序时,每个类定义通常放在头文件中,类的成员函数定义放在相同基本名字的源代码文件(source-code file)中。在使用类的每个文件中包含头文件(通过#include),而源代码文件编译并连接包含主程序的文件。编译器文档中介绍了如何编译和连接由多个源文件组成的程序。

图 6.5 包含声明 Time 类的 time1.h 头文件、定义 Time 类成员函数的 Time1.cpp 文件和定义 main 函数的fig06_05.cpp文件。这个程序的输出与图 6.3 的输出相同。

1 // Fig. 6.5: timel.h
2 // Declaration of the Time class.
3 // Member functions are defined in timel.cpp
4
5 // prevent multiple inclusions of header file
6 #ifndef TIME1_H
7 #define TIME1_H
8
9 // Time abstract data type definition
10 class Time {
11 public:
12 Time(); // constructor
13 void setTime( int, int, int ); // set hour, minute, second
14 void printMilitary(); // print military time format
15 void printStandard(); // print standard time format
16 private:
17 int hour; // 0 - 23
16 int minute; // 0 59
19 int second; // 0 - 59
20 };
21
22 #endif
23 // Fig. 6.5: timel.cpp
24 // Member function definitions for Time class.
25 #include <iostream.h>
26 #include "time1.h"
27
28 // Time constructor initializes each data member to zero.
29 // Ensures all Time objects start in a consistent state.
30 Time::Time() { hour = minute = second = 0; }
31
32 // Set a new Time value using military time. Perform validity
33 // checks on the data values. Set invalid values to zero.
34 void Time::setTimm( int b, int m, int s )
35 {
36 hour = ( h >= 0 && h < 24 ) ? h : 0;
37 minute ( m >= 0 && m < 60 ) ? m : 0;
38 second = ( s >= 0 && s < 60 ) ? s : 0;
39 }
40
41 // Print Time in military format
42 void Time::printMilitary()
43 {
44 cout << (hourt< 10 ? "0" : "" ) << hour << ":"
45 ( minute < l0 ? "0" : "" ) << minute;
46 }
47
48 // Print time in standard format
49 void Time::printStandard()
50 {
51 cout << ( ( hour( == 0 || hour == 12 ) ? 12 : hourt% 12 )
52 << ":" << minute < l0 ? "0" : "" ) << mlnute
53 << ":" << ( second < l0 ? "0" : "" ) << second
54 << ( hour < 12 ? "AM" : "PM" );
55 }
56 // Fig. 6.5: fig06_05.cpp
57 // Driver for Timel class
58 // NOTE: Compile with timel.cpp
59 #include <iostream.h>
60 #include "time1.h"
61
62 // Driver to in main( test simple class Time
63 int main()
64 {
65 Time t; // instantiate object t of class time
66
67 cout << "The initial military time is";
68 t.printMilitary();
69 cout << "\nThe initial standard time is";
70 t.printStandardO;
71
72 t.setTime( 13, 27, 6 );
73 cout << "\n\nMilitary time after setTime is";
74 t.printMilitary();
75 cout << "%nStandard time after setTime is";
76 t.printStandard();
77
78 t.setTime( 99, 99, 99 ); // attempt invalid settings
79 count << "\n\nAfter attempting invalid settings:\n"
80 << "Military time:";
81 t.printMilitary();
82 cout << "\.Standard time:";
83 t.printStamdard();
84 cout << endl;
85 return 0;
86 }

输出结果:

The initial military time is 00:00
The initial standard time is 12:00:00 AM

Military time after setTime is 13:27
Standard time after setTime is 1:27:06 PM

After attempting invalid settings:
Military time: 00:00
Standard time: 12:00:00 AM

图 6.5 将 Time 类的接口与实现方法分离

注意类声明放在下列预处理代码中:

// prevent multiple inclusions of header file
#ifndef TIME1_H
#define TIME1_H
...
#defint

建立大程序时,其他定义和声明也放在头文件中。上述预处理指令使得在定义了 TIME1_H 名字时不再包含 #ifndef#endif 之间的代码。如果文件中原先没有包含头文件,则TIME1_H名字由#define指令定义,并使该文件包含头文件语句。如果文件中已经包含头文件,则TIME1_H名字已经定义,不再包含头文件语句。多次包含头文件语句通常发生在大程序中,许多头文件本身已经包含其他头文件。注意:预处理指令中符号化常量名使用的规则是把头文件名中圆点(.)换成下划线。

测试与调试提示 6.2
#ifdef#define#endif 预处理指令防止一个程序中多次包合相同的头文件。

编程技巧 6.2
头文件的 #ifdef#define 顸处理指令中用头文件名,井将圆点换成下划线,