为了更容易学习便于理解,我们的图例是以有两个小孩围成一圈,并且设置报数的数为1的情况来制作的。
上面的两种解决Josephus问题的解决办法从代码上来看,都属于一杆子到底的解法,第二种从结构表达上优于第一种,但是这两个都属于纯粹的
过程式程序设计,程序虽然简短,但很难让人看懂,程序的可读性不高,在我们没有学习面向对象的编程之前,聪明的人可能会把各各步骤分解出来做成由几个函数来解决问题。
思路大致可以分为以下六个部分:
1.建立结构
2.初始化小孩总数,和数小孩的数
3.初始化链表并构成环链
4.开始通过循环数小孩获得得胜者
5.输出得胜者
6.返回堆内存空间
从表上看这个程序为了便于阅读可以写成六个函数来分别处理这六个过程,的确,这么修改过后程序的可读性是提高了一大步,但是有缺点仍然存在,程序完全暴露在外,任何人都可以修改程序,程序中的一些程序作者不希望使用者能够修改的对象暴露在外,各对象得不到任何的保护,不能保证程序在运行中不被意外修改,对于使用者来说还是需要具备解决Josephus问题算法的能力,一旦程序变的越来越很,,每一个参与开发的程序员都需要通读程序的所有部分,程序完全不具备黑盒效应,给多人的协作开发带来了很大的麻烦,几乎每个人都做了同样的重复劳动,这种为了解决一个分枝小问题写一个函数,最后
由很多个解决局部问题的函数组合成的程序我们叫做
结构化程序设计,结构化编程较过程化编程相比可读性是提高了,但程序不能轻易的被分割解决一个个大问题的模块,在主函数中使用他们的时候总是这个函数调用到那个函数,如果你并不是这些函数的作者就很难正确方便的使用这些函数,而且程序的变量重名问题带来的困扰也是很让人头痛的。。。。。。。
那么面向对象的程序设计又是如何解决这些问题的呢?
面向对象的程序设计的思路是这样的:
程序 = 对象 + 对象 +对象..........
这么组合而来的
对于上面的josephus问题可以把问题分割成如下的方法进行设计(如下图所示)

由上图可以看出:
面向对象的程序设计是由类组合而成的,有类必然有类的对象,程序之间的交互主要是通过对象与对象之间的关系进行操作的。
由于我们把josephus问题分解成了josephus类和ring类,在主函数中,用户只需要使用josephus类设计其对象明确知道Josephus类的外部接口函数也就是操作该对象的方法initial()就可以了,至于josephus的内部实现又是如何与Ring类进行操作的使用者一概不需要知道,只要拿来用知道接口和接口函数是什么就可以了,这样的程序设计很好的保护了各类成员数据的安全,主函数代码调用极其简单只有建立对象和调用对象方法的操作这两部而已,以后类一旦需要修改,只修改类体本身就可以,而主函数不需要做任何修改,这样就很好的做到了什么人做的事情什么人处理互不冲突。
程序的代码如下,我把工程文件压缩了作为此帖的附件提供下载,希望读者仔细阅读仔细推敲,真正理解面向对象oop编程的特点和意图。
主程序test4.cpp
-----------------------------------------------------------
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <
iostream>
#include "josephus.h"
using namespace std;
void main()
{
Josephus a;
a.initial();
cin.get();
cin.get();
}
josephus.h
-----------------------------------------------------------
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
class Josephus
{
public:
Josephus(
int num=10,
int interval=1)
{
Josephus::num=num;
Josephus::interval=interval;
}
void initial();
protected:
int num;
int interval;
};
josephus.cpp
---------------------------------------------------------------
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <
iostream>
#include "josephus.h"
#include "ring.h"
using namespace std;
void Josephus::initial()
{
int num,interval;
cout<<"请输入孩子总数:";
cin>>num;
if(num<2)
{
cout<<"孩子总数不能小于2,否则不能构成环链!";
return;
}
cout<<"请输入抽选号码";
cin>>interval;
if(interval<1|interval>num)
{
cout<<"请输入抽选号码不能小于1或者大于小孩总数!";
return;
}
Josephus::num=num;
Josephus::interval=interval;
Ring a(num);
a.ShowRing(num);
cout<<endl;
for(
int i=1;i<num;i++)
{
a.CountInterval(interval);
a.ShowWiner_loser();
a.OutChild();
}
cout<<endl<<"胜利者是:";
a.ShowWiner_loser();
}
ring.h
-------------------------------------------------------------------
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
struct Children
{
int number;
Children *next;
};
class Ring
{
public:
Ring(
int num)
{
josephus
= new Children[num];
point
= josephus;
for(
int i=1;i<=num;i++)
{
point->number
= i;
point->next
= josephus
+ i % num;
point=point->next;
}
point
= &josephus[num-1];
}
~Ring()
{
delete[] josephus;
}
void ShowRing(
int num);
void CountInterval(
int interval);
void OutChild();
void ShowWiner_loser();
protected:
Children *josephus;
Children *point;
Children *cut_point;
};
ring.cpp
----------------------------------------------------------------------
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <
iostream>
#include "ring.h"
using namespace std;
void Ring::ShowRing(
int num)
{
point=josephus;
//也可以写成point=point->next;但前着效率高一点点
for(
int i=1;i<=num;i++)
{
cout<<point->number<<",";
point=point->next;
}
point=&josephus[num-1];
//输出过后恢复point应该在的位置
}
void Ring::CountInterval(
int interval)
//数小孩
{
for(
int i=0;i<interval;i++)
{
cut_point
= point;
point
= cut_point->next;
}
}
void Ring::OutChild()
{
cut_point->next
= point->next;
//将不要节点断离
point=cut_point;
}
void Ring::ShowWiner_loser()
{
cout<<point->number<<",";
}