青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

huaxiazhihuo

 

C++代碼(3)全排列

        本來打算寫24點的程序,以體現(xiàn)C++對算法表達的清晰性。但在此之前,得先解決一個N個數(shù)中取兩個數(shù)的組合問題,也即C(N,2),于是想到干脆連排列也一塊搞定,而且在討論全排列的時候,還牽扯到一個有趣的代碼問題,因此,就這樣專用一文來論排列與組合,內(nèi)容還相當(dāng)翔實。但是寫完全排列的時候,發(fā)現(xiàn)文章已經(jīng)很長了,只好打住,留待下期再論組合與部分排列。
        先來看一段有名代碼,稍作了改動,它出自于國外的算法書中,在國內(nèi)的算法書中到處可以看到它的身影,代碼本身確實非常巧妙,但其實,存在很大的問題。
template<typename T>
void Perm(T* pT, int n, int m)
{
    
if (n == m)
    
{
        
for (int i=0; i<m; i++)
            cout 
<< pT[i] << " ";
        cout 
<< endl;
        
return;
    }

    
else
    
{
        
for (int i=m; i<n; i++)
        
{
            swap(pT[m], pT[i]);
            Perm(pT, n, m
+1);
            swap(pT[m], pT[i]);
        }

    }

}
        循環(huán)中出現(xiàn)了遞歸,有點費解,好比多模板偏特化中又出現(xiàn)了多繼承還夾雜著虛函數(shù),但在這里,還是很好理解的,代碼按乘法原理來構(gòu)造,這也是排列算法的由來。計算N個數(shù)的全排列,就是針對內(nèi)中的任一個數(shù)實行一次N-1個數(shù)的全排列,即N!= N * (N-1)!。循環(huán)就是為了讓數(shù)組中的每個元素都參與到排列中來。首先取出第0個數(shù),對行實行了(N-1)!。然后取出第1個數(shù),其實就是將第1個數(shù)放到第0位中,第0個數(shù)放到第1個數(shù)中,通過Swap函數(shù),實行一次(N-1)!,之后,第1、0個再各就各位,返回原位。計算(N-1)!的時候,也按照N!那樣,對N-1個數(shù)的任一個數(shù)實行(N-2)!。完美的遞歸出現(xiàn)了,既然有遞歸,就有結(jié)束遞歸的代碼,遞歸結(jié)束在0!,也即是m==n時,表示已經(jīng)完成了一個排列。由于是循環(huán)中出現(xiàn)遞歸,遞歸中又出現(xiàn)了循環(huán),因此就算遞歸完成了,代碼還要繼續(xù)遞歸,遞歸到遞歸中的循環(huán)都結(jié)束了。當(dāng)年,我讀懂了這段代碼之后,馬上對遞歸的理解有了更深刻的認識。
        但是,這段代碼中存在著一個非常丑陋的缺陷,其輸出夾雜在操作之中,假如每一個排列的結(jié)果不是要顯示在控制臺上,而是要寫入文本,或者是顯示在窗口上,那么就必須修改這個完美遞歸的排列函數(shù),這無疑很不完美。當(dāng)然解決之法也不是沒有,使用函數(shù)對象,C中就只能用回調(diào)函數(shù),將其作為參數(shù)傳入permutate中,每一次遞歸完成,就祭出函數(shù)對象輸出排列的結(jié)果。Windows API中的很多枚舉函數(shù),例如EnumFont,EnumWindows都用了這法子。但我對這個法子很不感冒,它太不可控了;其次,還有另外一個問題,就是Swap中,假如對象很大,每一次交換則將耗費多少CPU資源,而permutate中,基本上是都是在Swap來Swap去;最后也是最大問題,這種方法只可用于全排列,對于部分排列,比如,求P(5,2),它完全無能為力,因此,必須另尋出路。
        ……
        上面省略了思考的過程,請恕我直接給出解決方案,其實很簡單。
        先從最簡單的排列對象開始考慮,即從0到N的排列數(shù),只要搞定了它的排列方式,就可以搞定所有對象的排列了,WHY?因為0到N可作為數(shù)組的索引,可能對這個答案有點迷糊,不要緊,繼續(xù)看下去。考察我們偉大的大腦是如何做排列的,請動筆,寫下4!的全部排列,看能否從中得到什么啟發(fā)。
        0123, 0132, 0213, 0231, 0312, 0321, 1023, 1032, …… ,3201,3210。終于寫完了0到3的全部全排列,手都酸了。可以發(fā)現(xiàn)一件事,就是按這種方式寫下來的排列,任何一個排列數(shù)的下一個排列數(shù)必定是唯一的,比如,2031的下一個就是2103,而不會是其他的排列,這就好像任何自然數(shù)N的有且必定有唯一一個后繼N+1,但是排列數(shù)就不一定都有后繼了,3210就是最后一個排列數(shù)了。于是,我們就可以像普通循環(huán)那樣寫代碼了,for(排列對象=0123; 排列對象!=3210; 排列對象.ToNext){輸出排列對象}。非常美妙吧,不過當(dāng)務(wù)之急,是先class Permutation。
class Permutation
{
public:
    
enum {MAX_SIZE = 10};
    Permutation(
int count)
    
{
        assert(
0 < count && count < MAX_SIZE);
        m_nCount 
= (char)count;
        
for (char i=0; i<m_nCount; i++)
            m_Indexs[i] 
= i;
    }


public:
    
bool ToNext();

    
char operator[] (int n)const
    
{
        assert(
0 <= n && n <= m_nCount);
        
return m_Indexs[n];
    }


private:
    
char m_Indexs[MAX_SIZE];
    
char m_nCount;
}
;
        以上是初版,只支持全排列,部分排列時,它的定義又將不同。我不想再解釋它為何是這個樣子,請各位用心揣摩,一切都那么必然。我再次用了呆板的數(shù)組的定義方式,就好像八皇后那樣,因為日常中使用的排列數(shù)很少會多于10個的,這一點我很放心,各位也大可以放心。排列后的結(jié)果m_Indexs,本來也打算像八皇后那樣,直接public出來,但考慮到這個類比八皇后更加通用,而且operator[]確實有點方便使用,寫代碼時,操作符能不重載就別重載。
        接下來,就要對付Permutation.DoNext,也即本文的主角。先考慮一個問題,為什么我們就知道02431的下一個排列必然是03124呢,這里面隱藏著什么奧秘嗎?仔細對比02431、03124這兩個排列的差異。發(fā)現(xiàn):02431第0位的0沒有變,但第1位的2變成了3,02431中2與3交換后,變成了03421。為什么是2呢?在02431中,1<3, 3<4, 但到了2時,則為4>2,所以2成了被指定的那個人。那為什么是3要與2交換呢,因為3后面的1<2,從后往前算,3是第一個比2大的數(shù)。所以,2與3交換,理所當(dāng)然,不可不戒。交換之后,02431變成了03421,再比較03124,就421與124不同,而且還互為逆序,OK,我們找到奧秘了。至于奧秘的理由,里面自有數(shù)學(xué)原因,但已經(jīng)無須關(guān)心了,我們的職任是編寫代碼。于是,代碼如下。代碼就不解釋了,請對照上面的描述理解代碼。
bool Permutation::ToNext()
{
    
char nReverse = m_nCount-1;
    
while (nReverse > 0 && m_Indexs[nReverse]<m_Indexs[nReverse-1])    
        nReverse
--;    // 尋找第一個要交換的元素
    if (nReverse == 0)    // 找不到,表示全排已經(jīng)完成
        return false;

    
char nSwap = m_nCount - 1;
    
while (m_Indexs[nSwap] < m_Indexs[nReverse-1])
        nSwap
--;    // 尋找第二個元素
    swap(m_Indexs[nReverse-1], m_Indexs[nSwap]);    // 開始交換
    reverse(m_Indexs+nReverse, m_Indexs+m_nCount);    // 逆順
    return true;
}

        main也不含糊,只為體現(xiàn)Permutation的用法。    
int main()
{
    
const int N = 3;
    Permutation perm(N);
    
const char* sTests[N] = {""""""};
    
do
    
{
        
for (int i=0; i<N; i++)
            cout 
<< sTests[perm[i]] << " ";
        cout 
<< endl;
    }
while(perm.ToNext());
    
return 0;
}

CLASS真是好東西,如果不用C++,而用C的話,我也不知道代碼會是什么樣子,起碼不會這么易于表達,除了設(shè)計好算法和數(shù)據(jù)結(jié)構(gòu),還必須多花些心思琢磨代碼的設(shè)計,這不是一件快樂的事情,而用C++寫代碼,則非常愜意。



posted @ 2011-07-15 14:41 華夏之火 閱讀(2424) | 評論 (3)編輯 收藏

C++代碼(2)八皇后問題

        八皇后實在太有名了,我也就不廢話了。利用回溯算法,不難寫出其代碼,相信各位同學(xué)也都干過了。那這篇文章還有何新意呢?難道是向各位展示在下的代碼要比各位好,絕對不是。只因用C++寫代碼的時候,很容易就陷入各種細節(jié)的糾纏中,必須牢記大刀闊斧地砍除無關(guān)緊要的細節(jié),始終堅持用C++清晰地表達解決問題的思路,嚴格遵守單一職責(zé)的規(guī)則,集中精力解決問題,不賣弄半點花巧。這是在下努力的一種嘗試。
        八皇后的解決方案,相信大家也都知道了,請恕我直奔主題了。
        由于長年累月沉浸于C++中,中毒已經(jīng)甚深了,碰到任何問題,都條件反射地將其大御八塊,然后用CLASS來組織。于是,非常自然的,我決定用類QueenGame來表示八皇后這個游戲,用Play來擺弄出一個安全棋局。當(dāng)然,不用CLASS,也依然可以寫出清晰的代碼,但是,感覺總是很不習(xí)慣。于是,main起來就非常簡單了。
int main()
{
    QueenGame game(
8);
    
if(game.Play())
        DisplayQueen(game);
    
return 0;
}

        為何不讓DisplayQueens成為QueenGame的成員,那是因為最后的結(jié)果不止顯示于控制臺,也可能要寫入文件中,也可能會繪制于窗口上,而且就算于控制臺上,也有多種輸出方式,種種可能,無窮無盡,QueenGame難道要用一大堆成員函數(shù)來應(yīng)付這些顯示要求,這無疑不切合實際,而且也將污染QueenGame的接口。當(dāng)一個類無法預(yù)料一件操作將如何發(fā)生的時候,就應(yīng)該把這個決定交給上層調(diào)用來決定好了,這也是C++一貫的作法,既然我不知道怎么辦,那就由用戶來決定好了。我們要保持清醒,QueenGame的職責(zé)只是要擺出讓各個Queen和平共處的局面,至于其他的事情,那都不屬于自己的事情。不在其位,不謀其政。
        接下來就要思考QueenGame里面有那些東東,除了Play,肯定還有一些其他東東,最起碼也應(yīng)該有些房子來供皇后們居住。最自然的想法是用一個二維的bool數(shù)組chessboard來表示棋盤,假如chessboard[i][j]為true,就表示皇后在上面。但是,目前市面上的作法是用數(shù)組來儲存N個皇后的位置,數(shù)組上的元素的索引表示皇后在棋盤上第幾行,其值對應(yīng)皇后在第幾列上,這種儲存方式相當(dāng)方便高效,二維數(shù)組如何搗鼓,都沒它方便。因此,可以這樣定義后宮的地點int m_QueenPos[N],代碼有點呆板,似乎應(yīng)該用使用指針或者引入vector,來靈活處理不同數(shù)量的N個皇后,但這樣做,將引入一些其他的復(fù)雜性,對于本道程序,大可以假設(shè)皇后不會太多,10個已經(jīng)足夠多了,犧牲一點點空間,來踢開這個細節(jié),換取程序的安全穩(wěn)定,我非常樂意。
        由于QueenGame可以Play不同數(shù)目的皇后,從1個到10個都可以,因此在QueenGame的構(gòu)造函應(yīng)該確立皇后的數(shù)量,同時,再提供一個函數(shù)GetQueenCount,用以獲取她們的數(shù)目。QueenGame的初版如下:
class QueenGame
{
public:
    
enum {MAX_QUEEN_COUNT = 10};
    QueenGame(
int queenCount);
public:
    
int GetQueenCount()const
    
bool Play();
private:
    
int m_QueenPos[MAX_QUEEN_COUNT];
}
;
有了這些信息,就可以開始實作DisplayQueen了。但在此之前,還要解決一個問題,就是QueenGame如何讓外部函數(shù)來訪問它的棋盤局面呢?我的作法是直接暴露m_QueenPos,這不是公然違背了面向?qū)ο蟮姆庋b規(guī)定嗎?這樣做,我不會感到一絲絲的不安,因為除此之外,沒有更好的辦法了,其他的種種方案,都屬無病呻吟。比如說,仿效標(biāo)準(zhǔn)庫作法,typename一個迭代器,然后再搗鼓出一個begin和end的函數(shù),這將要花費多少精力啊,這么做,僅僅是為了取悅封裝要求,而與原本要解決的問題根本是風(fēng)馬牛不相及。那么采用GetQueenPos返回m_QueenPos的地址呢?這與直接訪問m_QueenPos并沒有多大的區(qū)別,如果以為這樣就可以滿足封裝,就可以享受封裝帶來的好處,純屬在自欺欺人罷了。還是一個辦法,就是讓DisplayQueens成為QueenGame的friend,這樣就可以不破壞封裝性,但如DisplayQueens不要為QueenGame的函數(shù)成員類似,既然DisplayQueens要為友元,那么WriteQueens也應(yīng)為friend了,ShowQueens也應(yīng)為friend了,為了滿足封裝,搞出這么多花招,畫蛇添足。……,但是,這樣直接暴露內(nèi)部狀態(tài),總是不安全的,那也沒什么,只要訂下規(guī)則,類外的一切函數(shù)不允許修改類的數(shù)據(jù)成員就OK了。總之,我不想在如何訪問m_QueenPos這個小細節(jié)上耗費一丁點精力了,我的精力應(yīng)該集中在原本要解決的問題上。
void DisplayQueen(const QueenGame& game)
{
    
using namespace std;
    
int count = game.GetQueenCount();
    
for (int n = 0; n<count; n++)
    
{
        
for (int i=1; i<=count; i++)
        
{
            
if (game.m_QueenPos[n] == i)
                cout 
<< "Q";
            
else
                cout 
<< "+";
        }

        cout 
<< endl;
    }

    cout 
<< endl;
}
        好了,做足準(zhǔn)備工作了,終于來到問題核心了,實作QueenGame的Play函數(shù)。這可不是一件容易的事情,起碼不像前面的代碼那么容易好寫。來回顧一下我們聰明的大腦是如何處理這個問題的。面對著棋盤,我們手里拿著8個皇后,先把第1個皇后擺到第1行的第1列上開始,然后按照規(guī)則擺放第2個,也即是在第1個皇后的淫威之外給第2個皇后尋找第1個安身之所(總共有6個),然后再在第1、2個皇后的勢力范圍之外給第3個皇后謀求第1個住所,可知越往后,皇后們的生存空間將越來越狹窄,以至于在第N個皇后的時候,已無安身之所,說明第N-1個皇后的位置不恰當(dāng),將她挪到下一個地方,然后再嘗試擺上第N個皇后,如果嘗試遍了第N-1個皇后的住所,都無法給第N個皇后提供一個去處,說明第N-1個皇后的位置不當(dāng),回溯到第N-2個皇后上,然后擺上第N-1個皇后,用這個方法,最后終究能安頓好8個皇后。接下來,就是將其轉(zhuǎn)換成代碼,很明顯,這里出現(xiàn)了遞歸。代碼的關(guān)鍵在于,當(dāng)擺上了第N個皇后時,如何表達第N+1個皇后的擺法,當(dāng)無法擺上時,又該如何回溯到第N-1個皇后上去。當(dāng)然,該如何停止遞歸,也不能不考慮。
bool QueenGame::Play()
{
    
int& lastPos = m_QueenPos[m_nLast];
    
while (++lastPos <= m_nQueenCount)
    
{
        
if (testNewQueen())
            
break;
    }

    
if (lastPos > m_nQueenCount)
    
{
        m_nLast
--;
        
if (m_nLast<=0 && m_QueenPos[0> m_nQueenCount)
            
return false;
    }

    
else
    
{
        
if (m_nLast+1 == m_nQueenCount)
            
return true;
        m_nLast
++;
        m_QueenPos[m_nLast] 
= 0;    
    }

    
return Play();
}

        代碼用m_nLast紀錄Play到第幾行了。其實這個變量可以省掉的,只要重新再寫一個Play的輔助函數(shù)PlayHelper,其帶有m_nLast的參數(shù),內(nèi)部遞歸調(diào)用自己。但是,在下寫代碼的原則是,能少寫函數(shù)就少寫函數(shù),而且用了m_nLast之后,這個程序還有一個新的功能。于是,QueenGame的定義如下。
class QueenGame
{
public:
    
enum {MAX_QUEEN_COUNT = 10};

    QueenGame(
int queenCount)
    
{
        assert(queenCount 
> 0 && queenCount <= MAX_QUEEN_COUNT );
        m_nQueenCount 
= queenCount;
        m_nLast 
= 0;
        m_QueenPos[m_nLast] 
= 0;
    }


public:
    
int GetQueenCount()const
    
{
        
return m_nQueenCount;
    }

    
bool Play();

    
int m_QueenPos[MAX_QUEEN_COUNT];

private:
    
bool testNewQueen();

    
int m_nQueenCount;
    
int m_nLast;
}
;
        私有函數(shù)testNewQueen用以最后的位置是否適合第N個皇后居住,分別從縱向和斜向上予以考察,橫向就不用再考慮了,你應(yīng)該知道WHY的。     
bool QueenGame::testNewQueen()
{
    
int lastPos = m_QueenPos[m_nLast];
    
for (int i=0; i<m_nLast; i++)
    
{
        
if (m_QueenPos[i] == lastPos || abs(m_QueenPos[i]-lastPos)==(m_nLast-i))
            
return false;
    }

    
return true;
}

        激動人心的一刻來臨了,程序終于可以跑起來了。再次審視代碼的時候,我們驚喜地發(fā)現(xiàn)QueenGame的Play函數(shù)可以遍歷所有的解,只要將main中的if改成while就可以了,非常棒。這都是堅持分離代碼的操作與輸出,并序?qū)嘶屎髥栴}封裝成類所帶來的好處。
        顯然,這里采用了深度優(yōu)先的搜索算法,代碼也可以寫成不用遞歸的形式,還有,這里也可以用寬度優(yōu)先搜索法,這一切就有待各位嘗試了。
        又,程序采用了一點點匈牙利的命名習(xí)慣,這是MFC用久了所沾染上的惡習(xí),沒辦法改了,偶也知道匈牙利命名的種種弊端。

posted @ 2011-07-13 19:12 華夏之火 閱讀(3234) | 評論 (4)編輯 收藏

質(zhì)樸的C++代碼(1)因數(shù)分解

            本節(jié)的代碼編寫,足以顯示中規(guī)中矩的質(zhì)樸的代碼風(fēng)格, 雖不十分高明,但起碼無屬大雅,在下自問對得起黨,對得國家,對得起人民。本文的任務(wù)是要顯示一個自然數(shù)的質(zhì)因數(shù)分解的結(jié)果,不需要涉及算法數(shù)據(jù)分析,所以可以集中精力專注于代碼的設(shè)計上。
            一般來說,程序可分為三部分,輸入,運算,輸出。所以,編寫程序的首要任務(wù)是將程序分解為三大部件,逐一地理解它們。
輸入:為一個自然數(shù),為簡化起見,可直接在代碼中寫死進一個變量當(dāng)中。
運算:將該自然數(shù)分解為一串質(zhì)數(shù),用數(shù)組來儲存這些質(zhì)因子,不失為一良策
輸出:顯示該數(shù)的串質(zhì)因子的連乘的式子,好比:
78 = 2 * 3 * 13
又或者
13 = 13
按此思想,可以快速地寫出這個程序的骨架了。

int main()
{
    
const int NUM_SIZE = 10;
    
int num = 1178;
    
int products[NUM_SIZE] = 0 };
    
    
int count = factorize(num, products);
    display(num, products, count);
    
return 0;
}

            似乎輸出部分較易解決,先搞掂它。代碼非常直白易懂,如果你堅持看不懂,只能說,你不適合寫代碼,學(xué)點其他的吧,你的專長不在這里。

void display(int num, int products[], int count)
{
    
using namespace std;
    assert(count 
> 0);
    cout 
<< num << " = " << products[0];
    
for (int i=1; i<count; i++)
        cout 
<< " * " << products[i];
    cout 
<< endl;
}

            很明顯,factorize為本程序的運算部分,也為核心所在,其中數(shù)組的大小并沒有傳進去,那是故意的,因為這樣可以減少很多不必要的代碼,當(dāng)函數(shù)對數(shù)組的大小要求不大時,我們完全可以假設(shè)數(shù)組足夠大,這足以解決大部分的問題,特別是寫自己模塊的時候。如果事事都要求吹毛求疵,那是相當(dāng)痛苦的事情。
            那么該如何分解質(zhì)因數(shù)呢?撇開代碼,先思考我們自己是如何分解質(zhì)因數(shù)的。如果用人腦都無法解決此問題,就算搬出多少流程圖,多少算法語言,都無濟于事,如果有了問題的算法,最清晰的表達方式依然是代碼本身。自打接觸程序設(shè)計到現(xiàn)在,我一直嚴重鄙視流程圖等東西。幸好因數(shù)分解的方法并不難,其法如下:從2開始,一個質(zhì)數(shù)一個質(zhì)數(shù)地逐個整除待分解之?dāng)?shù)N,如果取遍了N以內(nèi)的所有質(zhì)數(shù),都無法整除它,即表示N是一質(zhì)數(shù),分解結(jié)束。如果可以整除,那就表示該質(zhì)數(shù)為N之一因子,將其記下來,N也隨之取為整除的商,再次重復(fù)此步驟,一直到N變成一質(zhì)數(shù)。最后匯總所有能夠整除的質(zhì)數(shù)因子,連同最后的N。還沒忘記因數(shù)分解的同學(xué),相信會明白上面的意思。
            現(xiàn)在的問題,是如何將上面的算法翻譯成C++表達式。琢磨一下,發(fā)現(xiàn)最后匯總質(zhì)數(shù)因子的時候,還要匯總最后的N,這兩步其實是同一回事,其實當(dāng)N為質(zhì)數(shù)時,N為其自身的一因子,因此,最后一步,可直接簡化為匯總?cè)康馁|(zhì)數(shù)因子,只要在整除的過程中,多做一步運算,將N存起來即可。因此,上面的分解方法可變換為,用N以內(nèi)包括N本身的所有質(zhì)數(shù)整除N,重復(fù)此整除過程,直到N變?yōu)?為止。很明顯,這對應(yīng)于一個循環(huán),且此循環(huán)的條件是必須N>1。
           接下來,就要考慮當(dāng)質(zhì)數(shù)能夠整除N時,程序?qū)⒆瞿切┦虑椤?、N = N/質(zhì)數(shù);2、記下質(zhì)數(shù)。
            那么是否必須要求用質(zhì)數(shù)來整除N呢?其實沒必要,只要用小于或等于N以內(nèi)的所有大于1的自然數(shù)來整除N就可以保證到N以內(nèi)的所有質(zhì)數(shù)了,這樣雖然效率低了那么一點點,但代碼更易于編寫,清晰度更高,編碼時一定要抵制那種不顧一切地優(yōu)化的沖動。無傷大雅之時,沒必要精益求精。因數(shù)分解的代碼如下: 

int factorize(int num, int products[])
{
    assert(num 
> 1);
    
const int MINNEST_PRIME = 2;
    
int count = 0;
    
while (num > 1)
    
{
        
int prime = MINNEST_PRIME;
        
while (num%prime != 0)
            prime
++;
        num 
/= prime;
        products[count
++= prime;
    }

    
return count;
}

 

將以上代碼組織起來,添加必要的頭文件,感受一下辛苦的勞動果實吧,很有成就感!

posted @ 2011-07-11 12:06 華夏之火 閱讀(3348) | 評論 (5)編輯 收藏

C++雜談

       C++是我最喜歡的語言,它集自由、博大、復(fù)雜、簡約、高效于一身,又能很好地均衡這些特點,使它們和平共處,將“不為用到的任何特性付出一點點代價”的宗旨貫徹到底,其他的任何一種語言的都不具備像C++這樣的內(nèi)涵,使用C++之時,直有C++在手,江山我有的感覺。C雖然能讓你掌管一切,但用C開發(fā),有如戴著鐐銬在跳舞,無時不刻要小心翼翼地人肉管理一切細節(jié),實在太累了。而用C#、JAVA等其他語言時,雖然養(yǎng)尊處優(yōu),但想走不尋常路之時,又處處受限制,很有點寄人籬下的味道,未免不痛快。只有C++,既能下,又能上,進可攻,退可守,想怎么樣就怎么樣,盡情地飛翔。只要你愿意,你就可以在這一片世界里隨心所欲地發(fā)揮你的一切聰明才智,創(chuàng)造出種種奇技淫巧,而不會受到一點點約束。問題來了,自由得過頭了,就失去了控制,自由未必是好事。好多人,自由得甚至忘記了他要用C++的根本目的是什么,于是,C++到了他的手里,就變成為自由而自由,為復(fù)雜而復(fù)雜的利器,不但用它來折磨自己,還用它來迷惑別人,以致于忽視了原本要解決的問題,這樣一來,問題就很嚴重了。好的工具本來就是要用來做點實事的,做不了實事,要拿什么來證明自己呢?

        對于C++,沒什么好說的。但C++的教育,就有太多的不滿,要指責(zé)之處,也實在太多了,其中最為人詬病,就是很多C++的教材,都鼓勵讀者將注意力集中到C++的細節(jié)中,而忘記了如何用C++來簡潔質(zhì)樸地來表達設(shè)計思路。關(guān)于這一點,很多達人君子也已經(jīng)一再嚴厲地批評再批評。我也不想重復(fù)他們的論調(diào),只想舉兩個例子。

        C++因缺乏GC,而廣受非議。但內(nèi)存管理,其實不值得議論再議論,只要設(shè)計編寫得當(dāng),少耍小聰明,代碼中出現(xiàn)new和delete的次數(shù)可以很少很少,就算出現(xiàn),也只會出現(xiàn)于底層代碼中。為了彌補GC的缺席,C++界中發(fā)明了種種內(nèi)存管理的巧妙手法,其中最得力的一種辦法就是智能指針,而出現(xiàn)于標(biāo)準(zhǔn)庫中就是大名鼎鼎的auto_ptr了,甚至有人說,一本C++的教材,只要不介紹auto_ptr,就不屬于合格的教科書。但其實,auto_ptr并不見得那么重要,好比以下的代碼
Int* pa = new int;
……
delete pa;
        這代碼確實不好,于是該auto_ptr上場表演,變成
auto_ptr<int*> pa(new int);
        delete消失了,何其美妙,但其實,最樸實的代碼,連new都可以不用的,既然沒有new,就不需要auto_ptr了,最簡潔的代碼,非常簡單。
Int a = 0;
        一行就好,什么都用不了,很多出現(xiàn)auto_ptr的地方,直接用局部變量就可以了。不能使用局部變量的地方,就屬復(fù)雜的內(nèi)存管理了,在那里分配,在那里釋放,都很有必要細細地斟酌一番,auto_ptr并非什么萬能丹,一有內(nèi)存分配,就搬出auto_ptr,只怕屬本本主義的作風(fēng)。即此以觀,什么share_ptr,scope_ptr,也就那么一點點作用而已,無須大書特書。

        我承認,BOOST精妙無比,那都是C++程序聰明才智的結(jié)晶,但其實,真正搬得上臺面,發(fā)揮大作用的玩意,為數(shù)并不多,好比Tuple,可以方便地返回函數(shù)中的多個結(jié)果,例如……(請大家自己動手,或baidu或google),乍聽起來,似乎美妙無比。但其實,沒什么作用,什么時候,我們需要從函數(shù)中返回多個值?需要從函數(shù)中返回多值時,我會盡量地寫本地代碼,實在必須調(diào)用函數(shù)了,只好搬出指針或引用,將參數(shù)傳遞進去,如果數(shù)量太多了,那就動用結(jié)構(gòu),用結(jié)構(gòu)組織這些返回值,這一切,做起來,并沒什么太大的不便。但是如果動用Tuple返回多個結(jié)果,可能方便了那么一點點,卻將導(dǎo)致代碼難以維護,因為Tuple里面的值各表示了什么意思,無法直接從代碼中看得出來,用過Tuple的同學(xué)自然知道我要說什么。Tuple的實現(xiàn)非常巧妙,如此費盡心思弄出來的東西,不過是一只漂亮花瓶而已,真讓人扼腕嘆息不已,很多C++的庫,尤其是BOOST,都是這個樣子,看起來很精致,用起來,卻完全不是那么一回事,而且還引入很多不必要復(fù)雜性,世人稱心智包袱。

        ……
        用C++做設(shè)計,很容易就導(dǎo)致庫設(shè)計,如果設(shè)計出來的庫有用好用,那也罷了,問題是費了九牛二虎之力,搞出來的東西,半點得不到別人的認可,甚至連自己都無法認可,那就太不應(yīng)該了。
        用C++寫代碼,老老實實地寫代碼,不要忘記了編程的用意,別沉浸于語言中,盡量將代碼寫得直白易懂,少賣弄聰明才智, 慎用C++的一切特性,繼承、虛函數(shù)、操作符重載、模板、異常、new delete、……,更加不要用它們創(chuàng)造出什么奇技淫巧,必須用它們的時候,必須要有使用它們的理由。確實存在必須使用它們的理由,還堅決不用,那就是傻瓜和偏執(zhí)狂了,這不是合格的C++碼農(nóng),C++雖然不喜歡胡作非為的搗蛋鬼,但也杜絕一切墨守成規(guī)的書呆子。

posted @ 2011-07-11 09:29 華夏之火 閱讀(2836) | 評論 (30)編輯 收藏

僅列出標(biāo)題
共5頁: 1 2 3 4 5 

導(dǎo)航

統(tǒng)計

常用鏈接

留言簿(6)

隨筆分類

隨筆檔案

搜索

積分與排名

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲另类在线视频| 亚洲女ⅴideoshd黑人| 久久国产精品色婷婷| 亚洲影院污污.| 精品成人一区| 女同性一区二区三区人了人一| 久久久久亚洲综合| 亚洲精品一二| 亚洲影音一区| 亚洲第一色在线| 精品91免费| 欧美国产日本韩| 欧美午夜国产| 美女精品网站| 欧美日韩在线高清| 久久久www免费人成黑人精品| 久久久青草青青国产亚洲免观| 亚洲巨乳在线| 亚洲欧美日韩精品久久久| 精品成人一区二区三区| 亚洲每日在线| 国产综合色在线| 亚洲国产精品一区二区尤物区| 欧美jizz19hd性欧美| 一区二区三区四区国产精品| 欧美一区二区大片| 一区二区不卡在线视频 午夜欧美不卡在 | 国产精品欧美日韩一区二区| 欧美成人免费观看| 国产精品久久久久aaaa樱花| 亚洲第一二三四五区| 亚洲美女在线观看| 精品1区2区| 亚洲一区二区精品| 亚洲精品久久久久久久久久久久久 | 亚洲欧美久久久久一区二区三区| 久久久久久亚洲综合影院红桃| 亚洲一区中文| 欧美成人免费观看| 免费成人av在线| 国产精品资源| 一区二区三区视频在线看| 最新日韩中文字幕| 久久亚洲一区二区三区四区| 亚洲永久免费av| 欧美日韩精品在线观看| 欧美国产精品劲爆| 在线播放日韩| 久久成人精品视频| 久久激情五月丁香伊人| 国产精品久在线观看| 日韩一本二本av| 亚洲欧洲日产国产综合网| 欧美在线网站| 久久电影一区| 国产精品嫩草99a| 中国av一区| 亚洲欧美一区二区三区久久 | 国产女人aaa级久久久级| 一本色道久久88综合亚洲精品ⅰ | 欧美精品v日韩精品v国产精品 | 亚洲免费一在线| 国产精品久久久久久久午夜| 一本色道久久综合亚洲精品不| 日韩午夜av| 欧美精品videossex性护士| 亚洲激情小视频| 99热免费精品| 欧美色视频日本高清在线观看| 日韩午夜精品视频| 亚洲一区二区三区国产| 欧美无砖砖区免费| 亚洲尤物影院| 久久综合久色欧美综合狠狠 | 在线观看一区欧美| 蜜桃av综合| 99国内精品久久| 欧美一级黄色录像| 黄色一区二区三区四区| 蜜臀av一级做a爰片久久| 亚洲精品国产品国语在线app | aa亚洲婷婷| 国产精品久久福利| 久久精品天堂| 免费久久99精品国产| 久久国产精品久久久久久久久久| 国产精品高潮久久| 欧美中文字幕| 欧美激情一区二区三区全黄| 一区二区三区视频在线看| 国产精品一区一区| 久久综合激情| 夜色激情一区二区| 久久婷婷丁香| 亚洲午夜高清视频| 国产亚洲成av人片在线观看桃| 久久综合综合久久综合| 99成人免费视频| 免费在线亚洲| 亚洲免费在线播放| 亚洲国产视频一区| 国产伦精品一区二区三| 美女爽到呻吟久久久久| 亚洲一区二区三区精品动漫| 久久香蕉国产线看观看av| 日韩视频在线观看一区二区| 国产日韩欧美中文| 欧美日韩一区在线| 老司机精品久久| 午夜国产精品影院在线观看| 亚洲高清在线视频| 久久午夜激情| 欧美一区二区三区四区在线| 亚洲日本久久| 好吊妞**欧美| 国产欧美一区二区三区另类精品| 欧美电影免费网站| 久久午夜视频| 亚洲欧洲一区二区天堂久久| 国产日本欧美在线观看| 欧美午夜精品理论片a级大开眼界| 久久激情五月丁香伊人| 亚洲无限av看| aa亚洲婷婷| 亚洲看片网站| 91久久久一线二线三线品牌| 另类亚洲自拍| 久久综合九色综合久99| 久久精品盗摄| 久久国产精品72免费观看| 亚洲永久在线观看| 在线视频欧美日韩| 日韩视频一区二区三区在线播放| 亚洲国产日韩欧美在线99| 极品尤物av久久免费看 | 欧美激情乱人伦| 免费观看一级特黄欧美大片| 久久久www成人免费毛片麻豆| 校园激情久久| 欧美诱惑福利视频| 欧美一区成人| 久久久久久亚洲精品中文字幕| 欧美在线|欧美| 久久精品中文| 久久免费精品日本久久中文字幕| 久久精品国产亚洲5555| 久久精品国语| 美日韩精品视频| 欧美国产高清| 国产精品福利在线观看| 国产精品一区=区| 黑人极品videos精品欧美裸| 国内精品伊人久久久久av一坑| 黄色成人片子| 亚洲人成在线观看| 一区二区三区www| 性刺激综合网| 美女视频黄 久久| 亚洲日本免费| 亚洲色无码播放| 亚洲免费av网站| 精品福利电影| 一区二区三区在线视频免费观看| 在线不卡a资源高清| 亚洲激情国产| 亚洲一区二区免费看| 久久精品日韩一区二区三区| 欧美国产在线电影| 99人久久精品视频最新地址| 亚洲欧美激情诱惑| 欧美1区2区视频| 国产精品免费久久久久久| 国内外成人免费激情在线视频网站| 亚洲激情社区| 午夜免费久久久久| 欧美成va人片在线观看| 一区二区三区四区蜜桃| 久久精品国产99| 欧美日韩亚洲三区| 激情久久中文字幕| 一区二区日韩欧美| 免费成人黄色av| 一本色道**综合亚洲精品蜜桃冫| 久久精品2019中文字幕| 欧美日韩亚洲一区| 在线电影一区| 欧美一区二区三区四区高清 | 国产精品成人免费精品自在线观看| 国产一区日韩二区欧美三区| 一本色道久久综合狠狠躁的推荐| 久久久九九九九| 在线亚洲成人| 欧美成人午夜视频| 国产综合av| 先锋影音久久久| 日韩一区二区精品视频| 可以免费看不卡的av网站| 国产精品视频自拍| 在线一区二区三区四区| 欧美xart系列高清|