9.14 实例研究:类 Point、Circle 和 Cylinder
下面考察本章的一个练习,即点、圆、圆柱体的层次结构。我们首先开发并使用类 Point(图9.8),然后从类Point派生出类Circle(图9.9),最后从类Circle派生出类 Cylinder(图9.10)。
图9.8列出了类Point。图中的第1行到第17行是类 Point 的定义。可以看到,类Point的数据成员为protected。因此.当从类Point派生出类Circle时,类Circle的成员函数不必使用访问函数就能够直接引用坐标x和y,这样可使性能更好。
第18行到第39行定义了类 Point 的成员函数。第40行到第57行是类Point的驱动程序。程序中的 main 函数必须使用访问函数getX和getY读取protected数据成员x和y的值。要记住,protected 数据成员只能被类和其派生类的成员和友元访问。
1 // Fig. 9.8: point2.h
2 // Definition of class Point
3 #ifndef POINT2_H
4 #define POINT2_H
5
6 class Point {
7 friend ostream &operator<<( ostream &, const Point & );
8 public:
9 Point( int = 0, int = O ); // default constructor
10 void setPoint( int, int ); // set coordinates
11 int getX() const { return x; } // get x coordinate
12 int getY() const { return y; } // get y coordinate
13 protected: // accessible to derived classes
14 int x, y; // coordinates of the point
15 } ;
16
17 #endif
18 // Fig. 9.8: point2.cpp
19 // Member functions for class Point
20 #include <iostream.h>
21 #include "point2.h"
22
23 // Constructor for class Point
24 Point::Point( int a, int b ) { setPoint( a, b ); }
25
26 // Set the x and y coordinates
27 void Point::setPoint( int a, int b )
28 {
29 x = a;
30 y = b;
31 }
32
33 // Output the Point
34 ostream &operator<<( ostream &output, const Point &p )
35 {
36 output << '[' << p.x << ", "<< p.y << '] ';
37
38 return output; // enables cascading
39 }
40 // Fig. 9.8:fig09 08.cpp
41 // Driver for class Point
42 #include <iostream.h>
43 #include "point2.h"
44
45 int main()
47 Point p( 72, 115 ); // instantiate Point object p
48
49 // protected data of Point inaccessible to main
50 cout << "X coordinate is" << p.getx()
51 << "\nY coordinate is "<< p.getY();
52
53 p.setPoint( 10, 10 );
54 cout << "\n\nThe new location of p is "<< p << endl;
55
56 return 0;
57 }
输出结果:
X coordinate is 72
Y coordinate is 115
The new location of p is [ 10,10 ]
图 9.8 演示 Point 类
第二个例子是图 9.9。该例子复用了图 9.8 中的类 Point 的定义和成员函数定义,分别是类 Circle 的定义、类Circle的成员函数的定义和类的驱动程序。类Circle对类Point的继承是public继承,这意味着类Circle的public接口包括类Point的成员函数以及Circle成员函数 setRadius、getRadius 和 area。
注意 Circle 重载 operator<<函数,Circle类的友元可以通过将Circle引用c强制转换为 Point 而输出 Circle 的Point部分。因此调用 Point 的 operator<< 并用相应的Point格式输出x和y坐标。
驱动程序先实例化了类Circle的一个对象,然后用 get 函数读取该对象的信息。main 函数既不是类Circle的成员函数也不是其友元,因此它不能直接引用该类的protected数据。为此,程序中用"set"函数setRadius和setPoint重新设置圆的半径和圆心坐标。最后,驱动程序先将Point&(对Point对象的引用)类型的引用变量pRef初始化为Circle的对象c,然后打印出pRef,尽管它已经被初始化为一个 Circle 对象,但是它还“认为”自己是一个 Point 对象,所以该Circle对象实际上作为 Point 对象打印的。
1 // Fig. 9.9: circle2.h
2 // Definition of class Circle
3 #ifndef CIRCLE2_H
4 #define CIRCLE2_H
5
6 #include "point2.h"
7
8 class Circle : public Point {
9 friend ostream &operator<<( ostream &, const Circle & );
10 public:
11 // default constructor
12 Circle( double r = 0.0, int x = 0, int y = 0 );
13 void setRadius( double ); // set radius
14 double getRadius() const; // return radius
15 double area() const; // calculate area
16 protected: // accessible to derived classes
17 double radius; // radius of the Circle
18 };
19
20 #endif
21 // Fig. 9.9: circle2.cpp
22 // Member function definitions for class Circle
23 #include <iostream.h>
24 #include <iomanip.h>
25 #include "circle2.h"
26
27 // Constructor for Circle calls constructor for Point
28 // with a member initializer and initializes radius
29 Circle::Circle( double r, int a, int b )
30 : Point( a, b ) // call base-class constructor
31 { setRadius( r ); }
32
33 // Set radius
34 void Circle::setRadius( double r )
35 { radius = ( r >= 0 ? r : 0 ); }
36
37 // Get radius
38 double Circle::getRadius() const { return radius; }
39
40 // Calculate area of Circle
41 double Circle::area() const
42 { return 3.14159 * radius * radius; }
43
44 // Output a circle in the form:
45 // Center [ x, Y ]; Radius = #.##
46 ostream &operator<<( ostream &output, const Circle &c )
47 {
48 output << "Center = "<< static_cast< Point > ( c )
49 << "; Radius ="
50 << setiosflags( ios::fixed | ios::showpoint )
51 << setprecision( 2 ) << c.radius;
52
53 return output; // enables cascaded calls
54 }
55 // Fig. 9.9: fig09_09.cpp
56 // Driver for class Circle
57 #include <iostream.h>
58 #include "point2.h"
59 #include "circle2.h"
6O
61 int main()
62 {
63 Circle c( 2.5, 37, 43 );
64
65 cout << "X coordinate is "<< c.getX()
66 << "\nY coordinate is "<< c.getY()
67 << "\nRadius is "<< c.getRadius();
68
69 c.setRadius( 4.25 );
70 c.setPoint( 2, 2 );
71 cout << "\n\nThe new location and radius of C are\n"
72 << c << "\nArea "<< c.area() << '\ n';
73
74 Point &pRef = c;
75 cout << "\nCircle printed as a Point is: "<< pRef << endl;
76
77 return 0;
78 }
输出结果:
X coordinate is 37
Y coordinate is 43
Radius is 2.5
The new location and radius of c are
Center = [ 2,2 ]; Radius = 4.25
Area 56.74
Circle printed as a Point is: [ 2, 2 ]
图 9.9 演示 Circle 类型
最后一个例子是图9.10。该例复用了类Point和类Circle的定义以及图9.8和图9.9中的成员函数的定义,分别是类Cylinder的定义、Cylinder成员函数的定义以及类的驱动程序。类Cylinder对类Circle的继承是public继承,这意味着类Cylinder的public接口包括类Cylinder的成员函数、类Point的成员函数以及成员函数setHeight、getHeight、area(对Circle中的area重新定义)以及volume。
注意Cylinder构造函数要调用直接基类Circle的构造函数,而不调用间接基类Point的构造函数。每个派生类构造函数只负责调用直接基类的构造函数(多重继承中可能有多个直接基类)。另外,注意Cylinder重载operator<<函数,Cylinder类的友元可以通过将Cylinder引用c强制转换为Circle而输出
Cylinder的Circle部分。因此调用Circle的operator<<并用相应Circle格式输出x和y坐标。
驱动程序实例化了类Cylinder的一个对象,然后用“get”函数读取该对象的信息。main函数既不是类Cylinder的成员函数也不是其友元,因此它不能直接引用类Cylinder的protected数据。
为此,程序用"set"函数setHeight和setRadius以及setPoint重新设置圆柱体的高度、半径和坐标值。最后,驱动程序先把Point&(对Point对象的引用)类型的引用变量pRef初始化为类Cylinder的对象cyl,然后打印出pRef,尽管它已经被初始化为一个Cylinder对象,但是它还是“认为”自己是一个Point对象,所以该Cylinder对象实际上作为一个Point对象来打印;其次,将Circle&(对Circle的引用)类型的引用变量cRof初始化为Cylinder对象cyl,然后驱动程序打印cireleRef,尽管它已经被初始化为一个Cylinder对象,它还 认为 自己是一个Circle对象,所以该Cylinder对象实际上是作为一个Circle对象打印的,Circle的面积也同时打印出来。
这个例子很好地演示了public继承以及对protected数据成员的定义和引用,读者现在应该对继承的基本知识有了一定的了解。下一章要讨论如何用多态性编写具有继承层次结构的程序。数据抽象、继承和多态性是面向对象程序设计的关键所在。
1 // Fig. 9.10: cylindr2.h
2 // Definition of class Cylinder
3 #ifndef CYLINDR2_H
4 #define CYLINDR2_H
5
6 #include "circle2.h"
7
8 class Cylinder : public Circle {
9 friend ostream &operator<<( ostream &, const Cylinder & );
10
11 public:
12 // default constructor
13 Cylinder( double h = 0.0, double r = 0.0,
14 int x = 0, int y = 0 );
15
16 void setHeight( double ); // set height
17 double getHeight() const; // return height
18 double area() const; // calculate and return area
19 double volume() const; // calculate and return volume
2O
21 protected:
22 double height; // height of the Cylinder
23 };
24
25 #endif
26 // Fig. 9.10: cylindr2.cpp
27 // Member and friend function definitions
28 // for class Cylinder.
30 #include <iomanip.h>
31 #include "cylindr2.h"
32
33 // Cylinder constructor calls Circle constructor
34 Cylinder::Cylinder( double h, double r, int x, int y )
35 : Circle( r, x, y ) // call base-class constructor
36 { setHeight( h ); }
37
38 // Set height of Cylinder
39 void Cylinder::setHeight( double h )
40 { height = ( h >= 0 ? h : 0 ); }
41
42 // Get height of Cylinder
43 double Cylinder::getHeight() const { return height; }
44
45 // Calculate area of Cylinder (i.e., surface area)
46 double Cylinder::area() const
47 {
48 return 2 * Circle::area() +
49 2 * 3.14159 * radius * height;
5O }
51
52 // Calculate volume of Cylinder
53 double Cylinder::volume() const
54 { return Circle::area() * height; }
55
56 // Output Cylinder dimensions
57 ostream &operator<<( ostream &output, const Cylinder &c )
58 {
59 output << static_cast< Circle >( c )
60 << "; Height = "<< c.height;
61
62 return output; // enables cascaded calls
63 }
64 // Fig. 9.10: fig09_10.cpp
65 // Driver for class Cylinder
66 #include <iestream.h>
67 #include <iomanip.h>
69 #include "circle2.h"
70 #include "cylindr2.h"
72 int main()
73 {
74 // create Cylinder object
75 Cylinder cy1( 5.7, 2.5, 12, 23 );
76
77 // use get functions to display the Cylinder
78 cout << "X coordinate is " << cy1.getX()
79 << "\nY coordinate is "<< cy1.getY()
80 << "\nRadius is " << cy1.getRadius()
81 << "\nHeight is " << cy1.getHeight() << "\n\n";
82
83 // use set functions to change the Cylinder's attributes
84 cy1.setHeight(10);
85 cy1.setRadius(4.25);
86 cy1.setPoint(2,2);
87 cout << "The new location, radius, and height of cy1 are:\n"
88 << cy1 << '\n';
89
90 // display the Cylinder as a Point
91 Point &pRef = cy1; // pRef "thinks" it is a Point
92 cout << "\nCylinder printed as a Point is: "
93 << pRef << "\n\n";
94
95 // display the Cylinder as a Circle
96 Circle &circleRef = cy1; // circleef thinks it is a Circle
97 cout << "Cylinder printed as a Circle is:\n" << circleRef
98 << "\nArea: "<< circleRef.area() << endl;
99
100 return 0;
101 }
输出结果:
X coordinate is 12
Y coordinate is 23
Radius is 2.5
Height is 5.7
The new location, radius, and height of cy1 are:
Center = [ 2, 2 ]; Radius = 4.25; Height = 10.00
Cylinder printed as a Point is: [ 2, 2 ]
Cylinder printed as a Circle is:
Center = [2, 2] ; Radius = 4.25
Area: 56.74
图 9.10 演示 Cylinder 类