理解C++面向对象程序设计中的抽象理论

2016-02-19 19:59 6 1 收藏

想不想get新技能酷炫一下,今天图老师小编就跟大家分享个简单的理解C++面向对象程序设计中的抽象理论教程,一起来看看吧!超容易上手~

【 tulaoshi.com - 编程语言 】

很多书在一开始就开始学习josephus问题,为了让大家前面学起来较为轻易我把前面涉及到此问题的地方都故意去掉了,现在我们已经学习过了结构体和类,所以放在这里学习可能更合适一些。
  
   !-- frame contents -- !-- /frame contents --   在正式开始学习之前我们先回顾一下如何利用数组和结构体的方式来解决,最后我们再看一下如何利用面向对象的抽象理念进行解决此问题的程序设计,相互对比,找出效率最高,最轻易理解,最方便维护的程序来,说明利用面向对象的抽象理念进行程序设计的好处。
  
    josephus问题其实就是一个游戏,一群小孩围成一个圈,设置一个数,这个数是个小于小孩总数大于0的一个整数,从第一个小孩开始报数,当其中一个小孩报到你设置的那个数的时候离开那个圈,这样一来反复报下去,直到只剩下最后一个小孩的时候那个小孩就是胜利者,写程序来找出这个小孩。
  
  以下是数组方法:
  
  由于数组的限制我们必须预先假设好有多少个小孩,离开的小孩他自身设置为0来标记离开状态。
  
  代码如下:#include iostream 
  using namespace std; 
  void main() 
  { 
    const int num=10; 
    int interval; 
    int a[num]; 
    for(int i=0; inum; i++) 
    { 
      a[i]=i+1; 
    }  
      cout "please input the interval: "; 
    cin interval; 
    for(int i=0; inum; i++) 
    { 
      cout a[i] ","; 
    } 
      cout endl; 
   
  int k=1; 
  int p=-1; 
   
  while(1) 
  { 
      for(int j=0;jinterval;) 
      { 
          p=(p+1)%num; 
          if(a[p]!=0) 
          { 
              j++; 
          } 
      } 
      if(k==num) 
      { 
          break; 
      } 
      couta[p]","; 
      a[p]=0; 
      k++; 
  } 
  cout "No." a[p] " boy've won."; 
  cin.get(); 
  cin.get(); 
  }  就数组解决来看,程序简短但效率不高可读性也不好,此代码没有什么非凡之处主要依靠一个加1取模的方式往返到首位置,形成环链:p=(p+1)%num;。 更多内容请看C/C++技术专题  C/C++进阶技术文档  C/C++相关文章专题,或   以下是利用结构体的方法解决josephus问题:
  
  
  当我们学过结构体后,我们了解到结构体自身的成员指针可以指向自身对象的地址的时候,我们很轻易想到解决这个数学问题,用结构体来描述是再合适不过的了,用它可以很完美的描述环形链表。
  
  代码如下: !-- frame contents -- !-- /frame contents -- #include iostream   
  #include string   
  using namespace std;     
   
  strUCt Children 
  { 
      int number; 
      Children *next; 
  }; 
   
  void show(Children *point,int num)//环链输出函数 
  { 
      for(int i=1;i=num;i++) 
      { 
          coutpoint-number","; 
          point = point-next; 
      } 
  } 
   
  void main() 
  { 
      int num;//孩子总数 
      int interval;//抽选号码 
      cout"请输入孩子总数:"; 
      cinnum; 
      cout"请输入抽选号码:"; 
      cininterval; 
       
      Children *josephus = new Children[num];//设置圈的起点指针,并动态开辟堆空间用于存储数据 
   
      Children *point = josephus;//用于初化链表的指针,起始地址与josephus指针相同 
   
      for(int i=1;i=num;i++) 
      { 
          point - number = i; 
          point - next = josephus + i % num;//利用+1取模的方式设置节点的next指针,当到最后的时候自动指向到第一个,形成环链 
          point = point-next;//将位置移到下一饿节点也就是下一个小孩的位置 
      } 
   
      show(point,num); 
   
      Children *cut_point; 
      point=&josephus[num-1];//把起始指针设置在最后一个节点,当进入循环的时候就会从0开始,这样就好让不需要的节点脱离 
  
       int k=0;//故意设置一个k观察while循环了多少次 
      while(point-next!=point)//通过循环不断的寻找需要放弃的节点 
      { 
          k++; 
          for(int i = 0;iinterval;i++)//找需要放弃的节点位置 
          { 
              cut_point=point;//存储截断位置指针 
              point=cut_point-next;//将point的指针移动到放弃的节点位置,此处也和while循环终止条件有关系 
          } 
          cut_point-next=point-next;//将截断出的next指针设置成放弃处节点的next指针,使放弃处节点也就是不需要的节点脱离 
          cout"k:"kendl; 
      } 
      cout"最后的赢家:"endlpoint-numberendlpointendlpoint-nextendl; 
      delete[] josephus; 
      cin.get(); 
      cin.get(); 
  }  此代码较为难以理解的部分就是while循环的终止条件的设置,假如读者没有能够理解好这部分注重看下面的图式帮助学习。
  
  结构体的解法非常重要,对于我们全面理解面向对象的程序设计的抽象问题是基础,必须看明白我们才能够进行后面知识的学习,务必认真对待。
  
  这段代码比较前一个程序,可读性上有所加强,但仍然不太轻易理解! 更多内容请看C/C++技术专题  C/C++进阶技术文档  C/C++相关文章专题,或   为了更轻易学习便于理解,我们的图例是以有两个小孩围成一圈,并且设置报数的数为1的情况来制作的。  上面的两种解决Josephus问题的解决办法从代码上来看,都属于一杆子到底的解法,第二种从结构表达上优于第一种,但是这两个都属于纯粹的过程式程序设计,程序虽然简短,但很难让人看懂,程序的可读性不高,在我们没有学习面向对象的编程之前,聪明的人可能会把各各步骤分解出来做成由几个函数来解决问题。
  
  思路大致可以分为以下六个部分:
  
  1.建立结构
  
  2.初始化小孩总数,和数小孩的数
  
  3.初始化链表并构成环链
  
  4.开始通过循环数小孩获得得胜者
  
  5.输出得胜者
  
  6.返回堆内存空间
  
  从表上看这个程序为了便于阅读可以写成六个函数来分别处理这六个过程,的确,这么修改过后程序的可读性是提高了一大步,但是有缺点仍然存在,程序完全暴露在外,任何人都可以修改程序,程序中的一些程序作者不希望使用者能够修改的对象暴露在外,各对象得不到任何的保护,不能保证程序在运行中不被意外修改,对于使用者来说还是需要具备解决Josephus问题算法的能力,一旦程序变的越来越很,,每一个参与开发的程序员都需要通读程序的所有部分,程序完全不具备黑盒效应,给多人的协作开发带来了很大的麻烦,几乎每个人都做了同样的重复劳动,这种为了解决一个分枝小问题写一个函数,最后由很多个解决局部问题的函数组合成的程序我们叫做结构化程序设计,结构化编程较过程化编程相比可读性是提高了,但程序不能轻易的被分割解决一个个大问题的模块,在主函数中使用他们的时候总是这个函数调用到那个函数,假如你并不是这些函数的作者就很难正确方便的使用这些函数,而且程序的变量重名问题带来的困扰也是很让人头痛的……
  
   更多内容请看C/C++技术专题  C/C++进阶技术文档  C/C++相关文章专题,或   那么面向对象的程序设计又是如何解决这些问题的呢?
  
  
  面向对象的程序设计的思路是这样的:
  
  程序 = 对象 + 对象 +对象..........
  
  这么组合而来的
  
  对于上面的josephus问题可以把问题分割成如下的方法进行设计(如下图所示)   附件:点击下载(11K, zip压缩文件)  由上图可以看出:
  
  面向对象的程序设计是由类组合而成的,有类必然有类的对象,程序之间的交互主要是通过对象与对象之间的关系进行操作的。
  
  由于我们把josephus问题分解成了josephus类和ring类,在主函数中,用户只需要使用josephus类设计其对象明确知道Josephus类的外部接口函数也就是操作该对象的方法initial()就可以了,至于josephus的内部实现又是如何与Ring类进行操作的使用者一概不需要知道,只要拿来用知道接口和接口函数是什么就可以了,这样的程序设计很好的保护了各类成员数据的安全,主函数代码调用极其简单只有建立对象和调用对象方法的操作这两部而已,以后类一旦需要修改,只修改类体本身就可以,而主函数不需要做任何修改,这样就很好的做到了什么人做的事情什么人处理互不冲突。
  
  程序的代码如下,我把工程文件压缩了作为此帖的附件提供下载,希望读者仔细阅读仔细推敲,真正理解面向对象oop编程的特点和意图。
  
  主程序test4.cpp#include iostream 
  #include "josephus.h" 
  using namespace std; 
   
  void main() 
  { 
      Josephus a; 
      a.initial(); 
      cin.get(); 
      cin.get(); 
  }   josephus.hclass Josephus 
  { 
  public: 
       Josephus(int num=10,int interval=1) 
       { 
           Josephus::num=num; 
           Josephus::interval=interval; 
       } 
       void initial(); 
  protected: 
      int num; 
      int interval; 
  };  josephus.cpp#include iostream 
  #include "josephus.h" 
  #include "ring.h" 
   
  using namespace std; 
   
  void Josephus::initial() 
  { 
      int num,interval; 
      cout"请输入孩子总数:"; 
      cinnum; 
      if(num2) 
      { 
          cout"孩子总数不能小于2,否则不能构成环链!"; 
          return; 
      } 
      cout"请输入抽选号码"; 
      cininterval; 
      if(interval1intervalnum) 
      { 
          cout"请输入抽选号码不能小于1或者大于小孩总数!"; 
  
           return; 
      } 
      Josephus::num=num; 
      Josephus::interval=interval; 
      Ring a(num); 
      a.ShowRing(num); 
      coutendl; 
      for(int i=1;inum;i++) 
      { 
          a.CountInterval(interval); 
          a.ShowWiner_loser(); 
          a.OutChild(); 
      } 
      coutendl"胜利者是:"; 
      a.ShowWiner_loser(); 
  } 更多内容请看C/C++技术专题  C/C++进阶技术文档  C/C++相关文章专题,或   ring.hstruct Children 
  { 
      int number; 
      Children *next; 
  }; 
   
   !-- frame contents -- !-- /frame contents -- 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#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++) 
          { 
              coutpoint-number","; 
              point=point-next; 
          } 
          point=&josephus[num-1];//输出过后恢复point应该在的位置 
  } 
  void Ring::CountInterval(int interval)//数小孩 
  { 
      for(int i=0;iinterval;i++) 
      { 
          cut_point = point; 
          point = cut_point-next; 
      } 
  } 
  void Ring::OutChild() 
  { 
      cut_point-next = point-next;//将不要节点断离 
      point=cut_point; 
  } 
  void Ring::ShowWiner_loser() 
  { 
      coutpoint-number","; 
  }  程序中需要注重的小地方是在这里class Josephus 
  { 
  public: 
       Josephus(int num=10,int interval=1) 
       { 
           Josephus::num=num; 
           Josephus::interval=interval; 
       } 
       void initial(); 
  protected: 
      int num; 
      int interval; 
  };  代码中的 Josephus::num=num;
  Josephus::interval=interval;   使用域区分符的目的就是为了区分成员变量和局部变量Josephus(int num=10,int interval=1)
  
  相信读者认真读完程序认真理解后应该就可以理解面向对象程序设计的用意和好处了,切记认真推敲!
  
  大家看到面向对象程序设计的解决办法,可能觉得它的代码太多了,会怀疑它执行的效率是否足够好,呵呵!
  
  这里只能这么说,程序的效率不是单单看程序的长短来看的,优秀的程序应该是便于维护,关系清楚的,面向对象的程序设计其实和过程式或者是结构化程序设计的思路是不冲突的,在不同的地方使用不同的方法,优势互补才是正道! 更多内容请看C/C++技术专题 
    C/C++进阶技术文档  C/C++相关文章专题,或

来源:https://www.tulaoshi.com/n/20160219/1622461.html

延伸阅读
数组 前面说了在C++中是通过变量来对内存进行访问的,但根据前面的说明,C++中只能通过变量来操作内存,也就是说要操作某块内存,就必须先将这块内存的首地址和一个变量名绑定起来,这是很糟糕的。 !-- frame contents -- !-- /frame contents -- 比如有100块内存用以记录100个工人的工资,现在要将每个工人的工资增...
C++编程杂谈之二:面向对象 作者/xulion 软件开发是一个极其复杂的过程,一段小的代码我们可以快速、准确的完成,但是当你面对的是一个庞大的软件系统的时候,你是否有不知所措的感觉呢? 在我们使用C的年代里面,编程思想是结构化的,你的C语言老师可能会不断的教导你怎样使用结构化...
MFC的“文档/视图”机制最大的好处是可以很容易实现“一档多视”。但其内部机制是非常复杂,我们没有必要也不可能自己用托管C++方法去设计这样的机制,不过对于常用的文档、视图和窗口的经典问题,我们仍有必要对此进行深入探讨。这些问题包括:切分窗口、视图切换以及一档多视。限于篇幅,这里仅就切分窗口以及窗格之间的数据传送作深入...
上两篇内容我们着重说了结构体相关知识的操作。 以后的内容我们将逐步完全以c++作为主体了,这也意味着我们的教程正式进入面向对象的编程了。 !-- frame contents -- !-- /frame contents -- 前面的教程我已经再三说明,结构体的把握非常重要,重要在哪里呢?重要在结构体和类有相同的特性,但又...
一、什么是对象 学好VB的诀窍之一就是要以“对象”的眼光去看待整个程序设计。“对象”是面向对象程序设计的核心,明确这个概念对理解面向对象程序设计来说至关重要。那么,对象是什么?在程序中怎么没有看到对象呢? 其实对象在VB的程序设计中的地位,就像空气一样无处不在。以上一篇文章中的应用程序为例,程序中...

经验教程

730

收藏

73
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部