下面通过例子来说明面向对象程序设计的思维方式。
01、面向对象程序设计举例
【例 1】 运动场造价问题。运动场示意图如图 1 所示,其由中间的一个矩形足球场和两端的半圆形场地外加周边的跑道组成。已知足球场的长和宽以及跑道的宽度,且足球场的建造单价为 100 元/m2,空地和跑道的造价均为 80 元/m2。要求编程计算给定参数的运动场的造价。
▋图 1 运动场示意图
首先分析问题。问题的要求非常简单,就是输入运动场的参数,然后计算造价并输出,而运动场的参数只需要足球场的长和宽以及跑道宽度就可以了。进一步分析就会发现看到的事物或概念有运动场、足球场、半圆形场地、跑道、单价。而足球场、半圆形场地、跑道只是用来说明运动场的形状,对于问题的解答没有实质的帮助,因此用一个图形来描述运动场的形状即可(仅需要表示足球场的长和宽就可表示出运动场的基本图形),并且运动场与形状间有“有一个”关系。计算造价是运动场要提供的功能,为实现该功能需要计算各部分的面积,而计算各部分的面积是图形需要提供的功能。因此,计算造价时会向运动场对象询问,而运动场对象在计算造价时需要向图形对象询问各部分的面积。对于单价和跑道宽度,则可以作为运动场的属性。对于图形,其封装了表示足球场的长和宽,并提供计算完整图形(包括足球场和两端的半圆)的面积的功能、加上跑道宽度后整个图形面积的功能和足球场的面积的功能。综上,运动场与图形间的关系如图 2 所示。
▋图 2 运动场与图形间的关系
而计算造价可设计为运动场类的函数 compute_cost(),其运行过程可用 UML 的序列图表示,如图 3。
▋图 3 运动场类的 compute_cost()函数的序列图
首先实现 CPlaygraph 类,包括建立头文件 graph.h 和实现文件 graph.cpp:
//file: graph.h#ifndef __GRAPH_H__#define __GRAPH_H__ #include<iostream>#include<math.h>using namespace std; #define PI 3.14159 class CPlaygraph {public: CPlaygraph(const double len, const double w); //计算图形向外扩展ext后的面积 double full_area(const double ext = 0) const; double inner_area() const; //计算中间矩形的面积private: double m_length; double m_width;};#endif //file: graph.cpp:#include "graph.h" CPlaygraph::CPlaygraph(const double len, const double w) : m_length(len), m_width(w){} double CPlaygraph::full_area(double ext) const{ return m_length * (m_width + 2 * ext) + PI * (m_width / 2 + ext) * (m_width / 2 + ext);} double CPlaygraph::inner_area() const{ return m_length * m_width;}
复制代码
然后实现运动场类 CPlayground,包括建立 playground.h 文件和 playground.cpp 文件,其中足球场的建造单价和其他部分的建造单价作为该类的构造函数的默认参数给出。
//file: playground.h#ifndef __PLAYGROUND_H__#define __PLAYGROUND_H__#include "graph.h" class CPlayground{public: CPlayground(CPlaygraph & g, double lane, double cost1 = 100.0, double cost2 = 80.0); double compute_cost() const;private: CPlaygraph g; //存储运动场基本图形 const double LANE; //跑道宽度 const double COST1; //足球场的建造单价 const double COST2; //其他部分的建造单价};#endif //file: playground.cpp#include "Playground.h" CPlayground::CPlayground(CPlaygraph & g, double lane, double cost1, double cost2) : g(g), LANE(lane), COST1(cost1), COST2(cost2){} double CPlayground::compute_cost() const{ double cost = 0.0; cost += g.inner_area() * COST1; cost += (g.full_area(LANE) - g.inner_area()) * COST2; return cost;}
复制代码
最后,建立主程序所在的文件 main.cpp。
//main.cpp#include<conio.h>#include "Playground.h" int main(){ double len, w, lane; while (true) { cout << "请输入足球场的长、宽和跑道宽度:"; cin >> len >> w >> lane; CPlaygraph g(len, w); CPlayground ground(g, lane); cout << "造价是:" << ground.compute_cost() << endl; cout << "按 e 退出,按其他键继续..." << endl; if (_getch() == 'e') break; } return 0;}
复制代码
例子中,main()函数仍然是过程化的。事实上,类的每一个函数内部都是过程化的,所谓面向对象是站在较高的层次上说的。为使程序看起来更完全面向对象一点,可将应用程序本身抽象成类,这样程序的运行就是构造一个应用程序类的对象,该对象超出作用域析构后应用程序就结束了。如下面程序就定义了一个应用程序类 CApp,在 main.cpp 文件中定义了一个该类的对象 theApp,然后在 main()函数中通过 theApp 调用 CApp 类的 run()函数启动程序的执行。
//file: app.h#pragma once#include<iostream>#include<conio.h>#include"playground.h" class CApp{public: int run();}; //file: app.cpp#include "App.h" int CApp::run(){ double len, w, lane; while (true) { cout << "请输入足球场的长、宽和跑道宽度:"; cin >> len >> w >> lane; CPlaygraph g(len, w); CPlayground ground(g, lane); cout << "造价是:" << ground.compute_cost() << endl; cout << "按 e 退出,按其他键继续..." << endl; if (_getch() == 'e') break; } return 0;} //file: main.cpp#include"CApp.h" CApp theApp; int main(){ theApp.run(); return 0;}
复制代码
评论