国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

理解C++面向?qū)ο蟪绦蛟O(shè)計中的抽象理論

2019-11-17 05:04:59
字體:
供稿:網(wǎng)友
  很多書在一開始就開始學(xué)習(xí)josephus問題,為了讓大家前面學(xué)起來較為輕易我把前面涉及到此問題的地方都故意去掉了,現(xiàn)在我們已經(jīng)學(xué)習(xí)過了結(jié)構(gòu)體和類,所以放在這里學(xué)習(xí)可能更合適一些。

  在正式開始學(xué)習(xí)之前我們先回顧一下如何利用數(shù)組和結(jié)構(gòu)體的方式來解決,最后我們再看一下如何利用面向?qū)ο蟮某橄罄砟钸M(jìn)行解決此問題的程序設(shè)計,相互對比,找出效率最高,最輕易理解,最方便維護(hù)的程序來,說明利用面向?qū)ο蟮某橄罄砟钸M(jìn)行程序設(shè)計的好處。

  josephus問題其實就是一個游戲,一群小孩圍成一個圈,設(shè)置一個數(shù),這個數(shù)是個小于小孩總數(shù)大于0的一個整數(shù),從第一個小孩開始報數(shù),當(dāng)其中一個小孩報到你設(shè)置的那個數(shù)的時候離開那個圈,這樣一來反復(fù)報下去,直到只剩下最后一個小孩的時候那個小孩就是勝利者,寫程序來找出這個小孩。

  以下是數(shù)組方法:

  由于數(shù)組的限制我們必須預(yù)先假設(shè)好有多少個小孩,離開的小孩他自身設(shè)置為0來標(biāo)記離開狀態(tài)。

  代碼如下:#include <iostream> 
using namespace std; 
void main() 

  const int num=10; 
  int interval; 
  int a[num]; 
  for(int i=0; i<num; i++) 
  { 
    a[i]=i+1; 
  }  
    cout <<"please input the interval: "; 
  cin >>interval; 
  for(int i=0; i<num; i++) 
  { 
    cout <<a[i] <<","; 
  } 
    cout <<endl; 
 
int k=1; 
int p=-1; 
 
while(1) 

    for(int j=0;j<interval;) 
    { 
        p=(p+1)%num; 
        if(a[p]!=0) 
        { 
            j++; 
        } 
    } 
    if(k==num) 
    { 
        break; 
    } 
    cout<<a[p]<<","; 
    a[p]=0; 
    k++; 

cout <<"/nNo." <<a[p] <<" boy've won./n"; 
cin.get(); 
cin.get(); 
}  就數(shù)組解決來看,程序簡短但效率不高可讀性也不好,此代碼沒有什么非凡之處主要依靠一個加1取模的方式往返到首位置,形成環(huán)鏈:p=(p+1)%num;。 更多文章 更多內(nèi)容請看C/C++技術(shù)專題  C/C++進(jìn)階技術(shù)文檔  C/C++相關(guān)文章專題,或   以下是利用結(jié)構(gòu)體的方法解決josephus問題:


  當(dāng)我們學(xué)過結(jié)構(gòu)體后,我們了解到結(jié)構(gòu)體自身的成員指針可以指向自身對象的地址的時候,我們很輕易想到解決這個數(shù)學(xué)問題,用結(jié)構(gòu)體來描述是再合適不過的了,用它可以很完美的描述環(huán)形鏈表。

  代碼如下:
#include <iostream>   
#include <string>   
using namespace std;     
 
strUCt Children 

    int number; 
    Children *next; 
}; 
 
void show(Children *point,int num)//環(huán)鏈輸出函數(shù) 

    for(int i=1;i<=num;i++) 
    { 
        cout<<point->number<<","; 
        point = point->next; 
    } 

 
void main() 

    int num;//孩子總數(shù) 
    int interval;//抽選號碼 
    cout<<"請輸入孩子總數(shù):"; 
    cin>>num; 
    cout<<"請輸入抽選號碼:"; 
    cin>>interval; 
     
    Children *josephus = new Children[num];//設(shè)置圈的起點指針,并動態(tài)開辟堆空間用于存儲數(shù)據(jù) 
 
    Children *point = josephus;//用于初化鏈表的指針,起始地址與josephus指針相同 
 
    for(int i=1;i<=num;i++) 
    { 
        point -> number = i; 
        point -> next = josephus + i % num;//利用+1取模的方式設(shè)置節(jié)點的next指針,當(dāng)?shù)阶詈蟮臅r候自動指向到第一個,形成環(huán)鏈 
        point = point->next;//將位置移到下一餓節(jié)點也就是下一個小孩的位置 
    } 
 
    show(point,num); 
 
    Children *cut_point; 
    point=&josephus[num-1];//把起始指針設(shè)置在最后一個節(jié)點,當(dāng)進(jìn)入循環(huán)的時候就會從0開始,這樣就好讓不需要的節(jié)點脫離 

    int k=0;//故意設(shè)置一個k觀察while循環(huán)了多少次 
    while(point->next!=point)//通過循環(huán)不斷的尋找需要放棄的節(jié)點 
    { 
        k++; 
        for(int i = 0;i<interval;i++)//找需要放棄的節(jié)點位置 
        { 
            cut_point=point;//存儲截斷位置指針 
            point=cut_point->next;//將point的指針移動到放棄的節(jié)點位置,此處也和while循環(huán)終止條件有關(guān)系 
        } 
        cut_point->next=point->next;//將截斷出的next指針設(shè)置成放棄處節(jié)點的next指針,使放棄處節(jié)點也就是不需要的節(jié)點脫離 
        cout<<"k:"<<k<<endl; 
    } 
    cout<<"/n最后的贏家:"<<endl<<point->number<<endl<<point<<endl<<point->next<<endl; 
    delete[] josephus; 
    cin.get(); 
    cin.get(); 
}  此代碼較為難以理解的部分就是while循環(huán)的終止條件的設(shè)置,假如讀者沒有能夠理解好這部分注重看下面的圖式幫助學(xué)習(xí)。

  結(jié)構(gòu)體的解法非常重要,對于我們?nèi)胬斫饷嫦驅(qū)ο蟮某绦蛟O(shè)計的抽象問題是基礎(chǔ),必須看明白我們才能夠進(jìn)行后面知識的學(xué)習(xí),務(wù)必認(rèn)真對待。

  這段代碼比較前一個程序,可讀性上有所加強,但仍然不太輕易理解! 更多文章 更多內(nèi)容請看C/C++技術(shù)專題  C/C++進(jìn)階技術(shù)文檔  C/C++相關(guān)文章專題,或 理解C++面向?qū)ο蟪绦蛟O(shè)計中的抽象理論
  為了更輕易學(xué)習(xí)便于理解,我們的圖例是以有兩個小孩圍成一圈,并且設(shè)置報數(shù)的數(shù)為1的情況來制作的。  上面的兩種解決Josephus問題的解決辦法從代碼上來看,都屬于一桿子到底的解法,第二種從結(jié)構(gòu)表達(dá)上優(yōu)于第一種,但是這兩個都屬于純粹的過程式程序設(shè)計,程序雖然簡短,但很難讓人看懂,程序的可讀性不高,在我們沒有學(xué)習(xí)面向?qū)ο蟮木幊讨埃斆鞯娜丝赡軙迅鞲鞑襟E分解出來做成由幾個函數(shù)來解決問題。

  思路大致可以分為以下六個部分:

  1.建立結(jié)構(gòu)

  2.初始化小孩總數(shù),和數(shù)小孩的數(shù)

  3.初始化鏈表并構(gòu)成環(huán)鏈

  4.開始通過循環(huán)數(shù)小孩獲得得勝者

  5.輸出得勝者

  6.返回堆內(nèi)存空間

  從表上看這個程序為了便于閱讀可以寫成六個函數(shù)來分別處理這六個過程,的確,這么修改過后程序的可讀性是提高了一大步,但是有缺點仍然存在,程序完全暴露在外,任何人都可以修改程序,程序中的一些程序作者不希望使用者能夠修改的對象暴露在外,各對象得不到任何的保護(hù),不能保證程序在運行中不被意外修改,對于使用者來說還是需要具備解決Josephus問題算法的能力,一旦程序變的越來越很,,每一個參與開發(fā)的程序員都需要通讀程序的所有部分,程序完全不具備黑盒效應(yīng),給多人的協(xié)作開發(fā)帶來了很大的麻煩,幾乎每個人都做了同樣的重復(fù)勞動,這種為了解決一個分枝小問題寫一個函數(shù),最后由很多個解決局部問題的函數(shù)組合成的程序我們叫做結(jié)構(gòu)化程序設(shè)計,結(jié)構(gòu)化編程較過程化編程相比可讀性是提高了,但程序不能輕易的被分割解決一個個大問題的模塊,在主函數(shù)中使用他們的時候總是這個函數(shù)調(diào)用到那個函數(shù),假如你并不是這些函數(shù)的作者就很難正確方便的使用這些函數(shù),而且程序的變量重名問題帶來的困擾也是很讓人頭痛的……

更多文章 更多內(nèi)容請看C/C++技術(shù)專題  C/C++進(jìn)階技術(shù)文檔  C/C++相關(guān)文章專題,或   那么面向?qū)ο蟮某绦蛟O(shè)計又是如何解決這些問題的呢?


  面向?qū)ο蟮某绦蛟O(shè)計的思路是這樣的:

  程序 = 對象 + 對象 +對象..........

  這么組合而來的

  對于上面的josephus問題可以把問題分割成如下的方法進(jìn)行設(shè)計(如下圖所示) 理解C++面向?qū)ο蟪绦蛟O(shè)計中的抽象理論
  附件:點擊下載(11K, zip壓縮文件)  由上圖可以看出:

  面向?qū)ο蟮某绦蛟O(shè)計是由類組合而成的,有類必然有類的對象,程序之間的交互主要是通過對象與對象之間的關(guān)系進(jìn)行操作的。

  由于我們把josephus問題分解成了josephus類和ring類,在主函數(shù)中,用戶只需要使用josephus類設(shè)計其對象明確知道Josephus類的外部接口函數(shù)也就是操作該對象的方法initial()就可以了,至于josephus的內(nèi)部實現(xiàn)又是如何與Ring類進(jìn)行操作的使用者一概不需要知道,只要拿來用知道接口和接口函數(shù)是什么就可以了,這樣的程序設(shè)計很好的保護(hù)了各類成員數(shù)據(jù)的安全,主函數(shù)代碼調(diào)用極其簡單只有建立對象和調(diào)用對象方法的操作這兩部而已,以后類一旦需要修改,只修改類體本身就可以,而主函數(shù)不需要做任何修改,這樣就很好的做到了什么人做的事情什么人處理互不沖突。

  程序的代碼如下,我把工程文件壓縮了作為此帖的附件提供下載,希望讀者仔細(xì)閱讀仔細(xì)推敲,真正理解面向?qū)ο髈op編程的特點和意圖。

  主程序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<<"請輸入孩子總數(shù):"; 
    cin>>num; 
    if(num<2) 
    { 
        cout<<"孩子總數(shù)不能小于2,否則不能構(gòu)成環(huán)鏈!"; 
        return; 
    } 
    cout<<"請輸入抽選號碼"; 
    cin>>interval; 
    if(interval<1interval>num) 
    { 
        cout<<"請輸入抽選號碼不能小于1或者大于小孩總數(shù)!"; 

        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(); 
} 更多文章 更多內(nèi)容請看C/C++技術(shù)專題  C/C++進(jìn)階技術(shù)文檔  C/C++相關(guān)文章專題,或   ring.hstruct 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#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];//輸出過后恢復(fù)point應(yīng)該在的位置 

void Ring::CountInterval(int interval)//數(shù)小孩 

    for(int i=0;i<interval;i++) 
    { 
        cut_point = point; 
        point = cut_point->next; 
    } 

void Ring::OutChild() 

    cut_point->next = point->next;//將不要節(jié)點斷離 
    point=cut_point; 

void Ring::ShowWiner_loser() 

    cout<<point->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;   使用域區(qū)分符的目的就是為了區(qū)分成員變量和局部變量Josephus(int num=10,int interval=1)

  相信讀者認(rèn)真讀完程序認(rèn)真理解后應(yīng)該就可以理解面向?qū)ο蟪绦蛟O(shè)計的用意和好處了,切記認(rèn)真推敲!

  大家看到面向?qū)ο蟪绦蛟O(shè)計的解決辦法,可能覺得它的代碼太多了,會懷疑它執(zhí)行的效率是否足夠好,呵呵!

  這里只能這么說,程序的效率不是單單看程序的長短來看的,優(yōu)秀的程序應(yīng)該是便于維護(hù),關(guān)系清楚的,面向?qū)ο蟮某绦蛟O(shè)計其實和過程式或者是結(jié)構(gòu)化程序設(shè)計的思路是不沖突的,在不同的地方使用不同的方法,優(yōu)勢互補才是正道! 更多文章 更多內(nèi)容請看C/C++技術(shù)專題 
 C/C++進(jìn)階技術(shù)文檔  C/C++相關(guān)文章專題,或

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 宝清县| 寿光市| 玉林市| 义乌市| 万盛区| 尖扎县| 桐城市| 扎赉特旗| 梅州市| 额济纳旗| 开阳县| 龙井市| 通化县| 桦甸市| 二连浩特市| 星子县| 海兴县| 泰州市| 宜君县| 旅游| 凌海市| 武宁县| 太原市| 渝北区| 兴业县| 隆林| 桑植县| 揭西县| 锡林浩特市| 凌云县| 微山县| 金平| 成武县| 江津市| 兰溪市| 夏河县| 开原市| 枣庄市| 太仆寺旗| 砚山县| 丽江市|