huaxiazhihuo

 

C++代码(3)全排列

        本来打算写24点的程序,以体现C++对算法表达的清晰性。但在此之前,得先解决一个N个数中取两个数的组合问题,也即C(N,2),于是想到干脆连排列也一块搞定,而且在讨论全排列的时候,还牵扯到一个有趣的代码问题,因此,就这样专用一文来论排列与组合,内容还相当翔实。但是写完全排列的时候,发现文章已经很长了,只好打住,留待下期再论组合与部分排列。
        先来看一段有名代码,稍作了改动,它出自于国外的算法书中,在国内的算法书中到处可以看到它的身影,代码本身确实非常巧妙,但其实,存在很大的问题。
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]);
        }

    }

}
        循环中出现了递归,有点费解,好比多模板偏特化中又出现了多继承还夹杂着虚函数,但在这里,还是很好理解的,代码按乘法原理来构造,这也是排列算法的由来。计算N个数的全排列,就是针对内中的任一个数实行一次N-1个数的全排列,即N!= N * (N-1)!。循环就是为了让数组中的每个元素都参与到排列中来。首先取出第0个数,对行实行了(N-1)!。然后取出第1个数,其实就是将第1个数放到第0位中,第0个数放到第1个数中,通过Swap函数,实行一次(N-1)!,之后,第1、0个再各就各位,返回原位。计算(N-1)!的时候,也按照N!那样,对N-1个数的任一个数实行(N-2)!。完美的递归出现了,既然有递归,就有结束递归的代码,递归结束在0!,也即是m==n时,表示已经完成了一个排列。由于是循环中出现递归,递归中又出现了循环,因此就算递归完成了,代码还要继续递归,递归到递归中的循环都结束了。当年,我读懂了这段代码之后,马上对递归的理解有了更深刻的认识。
        但是,这段代码中存在着一个非常丑陋的缺陷,其输出夹杂在操作之中,假如每一个排列的结果不是要显示在控制台上,而是要写入文本,或者是显示在窗口上,那么就必须修改这个完美递归的排列函数,这无疑很不完美。当然解决之法也不是没有,使用函数对象,C中就只能用回调函数,将其作为参数传入permutate中,每一次递归完成,就祭出函数对象输出排列的结果。Windows API中的很多枚举函数,例如EnumFont,EnumWindows都用了这法子。但我对这个法子很不感冒,它太不可控了;其次,还有另外一个问题,就是Swap中,假如对象很大,每一次交换则将耗费多少CPU资源,而permutate中,基本上是都是在Swap来Swap去;最后也是最大问题,这种方法只可用于全排列,对于部分排列,比如,求P(5,2),它完全无能为力,因此,必须另寻出路。
        ……
        上面省略了思考的过程,请恕我直接给出解决方案,其实很简单。
        先从最简单的排列对象开始考虑,即从0到N的排列数,只要搞定了它的排列方式,就可以搞定所有对象的排列了,WHY?因为0到N可作为数组的索引,可能对这个答案有点迷糊,不要紧,继续看下去。考察我们伟大的大脑是如何做排列的,请动笔,写下4!的全部排列,看能否从中得到什么启发。
        0123, 0132, 0213, 0231, 0312, 0321, 1023, 1032, …… ,3201,3210。终于写完了0到3的全部全排列,手都酸了。可以发现一件事,就是按这种方式写下来的排列,任何一个排列数的下一个排列数必定是唯一的,比如,2031的下一个就是2103,而不会是其他的排列,这就好像任何自然数N的有且必定有唯一一个后继N+1,但是排列数就不一定都有后继了,3210就是最后一个排列数了。于是,我们就可以像普通循环那样写代码了,for(排列对象=0123; 排列对象!=3210; 排列对象.ToNext){输出排列对象}。非常美妙吧,不过当务之急,是先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;
}
;
        以上是初版,只支持全排列,部分排列时,它的定义又将不同。我不想再解释它为何是这个样子,请各位用心揣摩,一切都那么必然。我再次用了呆板的数组的定义方式,就好像八皇后那样,因为日常中使用的排列数很少会多于10个的,这一点我很放心,各位也大可以放心。排列后的结果m_Indexs,本来也打算像八皇后那样,直接public出来,但考虑到这个类比八皇后更加通用,而且operator[]确实有点方便使用,写代码时,操作符能不重载就别重载。
        接下来,就要对付Permutation.DoNext,也即本文的主角。先考虑一个问题,为什么我们就知道02431的下一个排列必然是03124呢,这里面隐藏着什么奥秘吗?仔细对比02431、03124这两个排列的差异。发现: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大的数。所以,2与3交换,理所当然,不可不戒。交换之后,02431变成了03421,再比较03124,就421与124不同,而且还互为逆序,OK,我们找到奥秘了。至于奥秘的理由,里面自有数学原因,但已经无须关心了,我们的职任是编写代码。于是,代码如下。代码就不解释了,请对照上面的描述理解代码。
bool Permutation::ToNext()
{
    
char nReverse = m_nCount-1;
    
while (nReverse > 0 && m_Indexs[nReverse]<m_Indexs[nReverse-1])    
        nReverse
--;    // 寻找第一个要交换的元素
    if (nReverse == 0)    // 找不到,表示全排已经完成
        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也不含糊,只为体现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的话,我也不知道代码会是什么样子,起码不会这么易于表达,除了设计好算法和数据结构,还必须多花些心思琢磨代码的设计,这不是一件快乐的事情,而用C++写代码,则非常惬意。



posted @ 2011-07-15 14:41 华夏之火 阅读(2377) | 评论 (3)编辑 收藏

C++代码(2)八皇后问题

        八皇后实在太有名了,我也就不废话了。利用回溯算法,不难写出其代码,相信各位同学也都干过了。那这篇文章还有何新意呢?难道是向各位展示在下的代码要比各位好,绝对不是。只因用C++写代码的时候,很容易就陷入各种细节的纠缠中,必须牢记大刀阔斧地砍除无关紧要的细节,始终坚持用C++清晰地表达解决问题的思路,严格遵守单一职责的规则,集中精力解决问题,不卖弄半点花巧。这是在下努力的一种尝试。
        八皇后的解决方案,相信大家也都知道了,请恕我直奔主题了。
        由于长年累月沉浸于C++中,中毒已经甚深了,碰到任何问题,都条件反射地将其大御八块,然后用CLASS来组织。于是,非常自然的,我决定用类QueenGame来表示八皇后这个游戏,用Play来摆弄出一个安全棋局。当然,不用CLASS,也依然可以写出清晰的代码,但是,感觉总是很不习惯。于是,main起来就非常简单了。
int main()
{
    QueenGame game(
8);
    
if(game.Play())
        DisplayQueen(game);
    
return 0;
}

        为何不让DisplayQueens成为QueenGame的成员,那是因为最后的结果不止显示于控制台,也可能要写入文件中,也可能会绘制于窗口上,而且就算于控制台上,也有多种输出方式,种种可能,无穷无尽,QueenGame难道要用一大堆成员函数来应付这些显示要求,这无疑不切合实际,而且也将污染QueenGame的接口。当一个类无法预料一件操作将如何发生的时候,就应该把这个决定交给上层调用来决定好了,这也是C++一贯的作法,既然我不知道怎么办,那就由用户来决定好了。我们要保持清醒,QueenGame的职责只是要摆出让各个Queen和平共处的局面,至于其他的事情,那都不属于自己的事情。不在其位,不谋其政。
        接下来就要思考QueenGame里面有那些东东,除了Play,肯定还有一些其他东东,最起码也应该有些房子来供皇后们居住。最自然的想法是用一个二维的bool数组chessboard来表示棋盘,假如chessboard[i][j]为true,就表示皇后在上面。但是,目前市面上的作法是用数组来储存N个皇后的位置,数组上的元素的索引表示皇后在棋盘上第几行,其值对应皇后在第几列上,这种储存方式相当方便高效,二维数组如何捣鼓,都没它方便。因此,可以这样定义后宫的地点int m_QueenPos[N],代码有点呆板,似乎应该用使用指针或者引入vector,来灵活处理不同数量的N个皇后,但这样做,将引入一些其他的复杂性,对于本道程序,大可以假设皇后不会太多,10个已经足够多了,牺牲一点点空间,来踢开这个细节,换取程序的安全稳定,我非常乐意。
        由于QueenGame可以Play不同数目的皇后,从1个到10个都可以,因此在QueenGame的构造函应该确立皇后的数量,同时,再提供一个函数GetQueenCount,用以获取她们的数目。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如何让外部函数来访问它的棋盘局面呢?我的作法是直接暴露m_QueenPos,这不是公然违背了面向对象的封装规定吗?这样做,我不会感到一丝丝的不安,因为除此之外,没有更好的办法了,其他的种种方案,都属无病呻吟。比如说,仿效标准库作法,typename一个迭代器,然后再捣鼓出一个begin和end的函数,这将要花费多少精力啊,这么做,仅仅是为了取悦封装要求,而与原本要解决的问题根本是风马牛不相及。那么采用GetQueenPos返回m_QueenPos的地址呢?这与直接访问m_QueenPos并没有多大的区别,如果以为这样就可以满足封装,就可以享受封装带来的好处,纯属在自欺欺人罢了。还是一个办法,就是让DisplayQueens成为QueenGame的friend,这样就可以不破坏封装性,但如DisplayQueens不要为QueenGame的函数成员类似,既然DisplayQueens要为友元,那么WriteQueens也应为friend了,ShowQueens也应为friend了,为了满足封装,搞出这么多花招,画蛇添足。……,但是,这样直接暴露内部状态,总是不安全的,那也没什么,只要订下规则,类外的一切函数不允许修改类的数据成员就OK了。总之,我不想在如何访问m_QueenPos这个小细节上耗费一丁点精力了,我的精力应该集中在原本要解决的问题上。
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;
}
        好了,做足准备工作了,终于来到问题核心了,实作QueenGame的Play函数。这可不是一件容易的事情,起码不像前面的代码那么容易好写。来回顾一下我们聪明的大脑是如何处理这个问题的。面对着棋盘,我们手里拿着8个皇后,先把第1个皇后摆到第1行的第1列上开始,然后按照规则摆放第2个,也即是在第1个皇后的淫威之外给第2个皇后寻找第1个安身之所(总共有6个),然后再在第1、2个皇后的势力范围之外给第3个皇后谋求第1个住所,可知越往后,皇后们的生存空间将越来越狭窄,以至于在第N个皇后的时候,已无安身之所,说明第N-1个皇后的位置不恰当,将她挪到下一个地方,然后再尝试摆上第N个皇后,如果尝试遍了第N-1个皇后的住所,都无法给第N个皇后提供一个去处,说明第N-1个皇后的位置不当,回溯到第N-2个皇后上,然后摆上第N-1个皇后,用这个方法,最后终究能安顿好8个皇后。接下来,就是将其转换成代码,很明显,这里出现了递归。代码的关键在于,当摆上了第N个皇后时,如何表达第N+1个皇后的摆法,当无法摆上时,又该如何回溯到第N-1个皇后上去。当然,该如何停止递归,也不能不考虑。
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的辅助函数PlayHelper,其带有m_nLast的参数,内部递归调用自己。但是,在下写代码的原则是,能少写函数就少写函数,而且用了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;
}
;
        私有函数testNewQueen用以最后的位置是否适合第N个皇后居住,分别从纵向和斜向上予以考察,横向就不用再考虑了,你应该知道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;
}

        激动人心的一刻来临了,程序终于可以跑起来了。再次审视代码的时候,我们惊喜地发现QueenGame的Play函数可以遍历所有的解,只要将main中的if改成while就可以了,非常棒。这都是坚持分离代码的操作与输出,并序将八皇后问题封装成类所带来的好处。
        显然,这里采用了深度优先的搜索算法,代码也可以写成不用递归的形式,还有,这里也可以用宽度优先搜索法,这一切就有待各位尝试了。
        又,程序采用了一点点匈牙利的命名习惯,这是MFC用久了所沾染上的恶习,没办法改了,偶也知道匈牙利命名的种种弊端。

posted @ 2011-07-13 19:12 华夏之火 阅读(3048) | 评论 (4)编辑 收藏

质朴的C++代码(1)因数分解

            本节的代码编写,足以显示中规中矩的质朴的代码风格, 虽不十分高明,但起码无属大雅,在下自问对得起党,对得国家,对得起人民。本文的任务是要显示一个自然数的质因数分解的结果,不需要涉及算法数据分析,所以可以集中精力专注于代码的设计上。
            一般来说,程序可分为三部分,输入,运算,输出。所以,编写程序的首要任务是将程序分解为三大部件,逐一地理解它们。
输入:为一个自然数,为简化起见,可直接在代码中写死进一个变量当中。
运算:将该自然数分解为一串质数,用数组来储存这些质因子,不失为一良策
输出:显示该数的串质因子的连乘的式子,好比:
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;
}

            似乎输出部分较易解决,先搞掂它。代码非常直白易懂,如果你坚持看不懂,只能说,你不适合写代码,学点其他的吧,你的专长不在这里。

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为本程序的运算部分,也为核心所在,其中数组的大小并没有传进去,那是故意的,因为这样可以减少很多不必要的代码,当函数对数组的大小要求不大时,我们完全可以假设数组足够大,这足以解决大部分的问题,特别是写自己模块的时候。如果事事都要求吹毛求疵,那是相当痛苦的事情。
            那么该如何分解质因数呢?撇开代码,先思考我们自己是如何分解质因数的。如果用人脑都无法解决此问题,就算搬出多少流程图,多少算法语言,都无济于事,如果有了问题的算法,最清晰的表达方式依然是代码本身。自打接触程序设计到现在,我一直严重鄙视流程图等东西。幸好因数分解的方法并不难,其法如下:从2开始,一个质数一个质数地逐个整除待分解之数N,如果取遍了N以内的所有质数,都无法整除它,即表示N是一质数,分解结束。如果可以整除,那就表示该质数为N之一因子,将其记下来,N也随之取为整除的商,再次重复此步骤,一直到N变成一质数。最后汇总所有能够整除的质数因子,连同最后的N。还没忘记因数分解的同学,相信会明白上面的意思。
            现在的问题,是如何将上面的算法翻译成C++表达式。琢磨一下,发现最后汇总质数因子的时候,还要汇总最后的N,这两步其实是同一回事,其实当N为质数时,N为其自身的一因子,因此,最后一步,可直接简化为汇总全部的质数因子,只要在整除的过程中,多做一步运算,将N存起来即可。因此,上面的分解方法可变换为,用N以内包括N本身的所有质数整除N,重复此整除过程,直到N变为1为止。很明显,这对应于一个循环,且此循环的条件是必须N>1。
           接下来,就要考虑当质数能够整除N时,程序将做那些事情。1、N = N/质数;2、记下质数。
            那么是否必须要求用质数来整除N呢?其实没必要,只要用小于或等于N以内的所有大于1的自然数来整除N就可以保证到N以内的所有质数了,这样虽然效率低了那么一点点,但代码更易于编写,清晰度更高,编码时一定要抵制那种不顾一切地优化的冲动。无伤大雅之时,没必要精益求精。因数分解的代码如下: 

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 华夏之火 阅读(3118) | 评论 (5)编辑 收藏

C++杂谈

       C++是我最喜欢的语言,它集自由、博大、复杂、简约、高效于一身,又能很好地均衡这些特点,使它们和平共处,将“不为用到的任何特性付出一点点代价”的宗旨贯彻到底,其他的任何一种语言的都不具备像C++这样的内涵,使用C++之时,直有C++在手,江山我有的感觉。C虽然能让你掌管一切,但用C开发,有如戴着镣铐在跳舞,无时不刻要小心翼翼地人肉管理一切细节,实在太累了。而用C#、JAVA等其他语言时,虽然养尊处优,但想走不寻常路之时,又处处受限制,很有点寄人篱下的味道,未免不痛快。只有C++,既能下,又能上,进可攻,退可守,想怎么样就怎么样,尽情地飞翔。只要你愿意,你就可以在这一片世界里随心所欲地发挥你的一切聪明才智,创造出种种奇技淫巧,而不会受到一点点约束。问题来了,自由得过头了,就失去了控制,自由未必是好事。好多人,自由得甚至忘记了他要用C++的根本目的是什么,于是,C++到了他的手里,就变成为自由而自由,为复杂而复杂的利器,不但用它来折磨自己,还用它来迷惑别人,以致于忽视了原本要解决的问题,这样一来,问题就很严重了。好的工具本来就是要用来做点实事的,做不了实事,要拿什么来证明自己呢?

        对于C++,没什么好说的。但C++的教育,就有太多的不满,要指责之处,也实在太多了,其中最为人诟病,就是很多C++的教材,都鼓励读者将注意力集中到C++的细节中,而忘记了如何用C++来简洁质朴地来表达设计思路。关于这一点,很多达人君子也已经一再严厉地批评再批评。我也不想重复他们的论调,只想举两个例子。

        C++因缺乏GC,而广受非议。但内存管理,其实不值得议论再议论,只要设计编写得当,少耍小聪明,代码中出现new和delete的次数可以很少很少,就算出现,也只会出现于底层代码中。为了弥补GC的缺席,C++界中发明了种种内存管理的巧妙手法,其中最得力的一种办法就是智能指针,而出现于标准库中就是大名鼎鼎的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;
        一行就好,什么都用不了,很多出现auto_ptr的地方,直接用局部变量就可以了。不能使用局部变量的地方,就属复杂的内存管理了,在那里分配,在那里释放,都很有必要细细地斟酌一番,auto_ptr并非什么万能丹,一有内存分配,就搬出auto_ptr,只怕属本本主义的作风。即此以观,什么share_ptr,scope_ptr,也就那么一点点作用而已,无须大书特书。

        我承认,BOOST精妙无比,那都是C++程序聪明才智的结晶,但其实,真正搬得上台面,发挥大作用的玩意,为数并不多,好比Tuple,可以方便地返回函数中的多个结果,例如……(请大家自己动手,或baidu或google),乍听起来,似乎美妙无比。但其实,没什么作用,什么时候,我们需要从函数中返回多个值?需要从函数中返回多值时,我会尽量地写本地代码,实在必须调用函数了,只好搬出指针或引用,将参数传递进去,如果数量太多了,那就动用结构,用结构组织这些返回值,这一切,做起来,并没什么太大的不便。但是如果动用Tuple返回多个结果,可能方便了那么一点点,却将导致代码难以维护,因为Tuple里面的值各表示了什么意思,无法直接从代码中看得出来,用过Tuple的同学自然知道我要说什么。Tuple的实现非常巧妙,如此费尽心思弄出来的东西,不过是一只漂亮花瓶而已,真让人扼腕叹息不已,很多C++的库,尤其是BOOST,都是这个样子,看起来很精致,用起来,却完全不是那么一回事,而且还引入很多不必要复杂性,世人称心智包袱。

        ……
        用C++做设计,很容易就导致库设计,如果设计出来的库有用好用,那也罢了,问题是费了九牛二虎之力,搞出来的东西,半点得不到别人的认可,甚至连自己都无法认可,那就太不应该了。
        用C++写代码,老老实实地写代码,不要忘记了编程的用意,别沉浸于语言中,尽量将代码写得直白易懂,少卖弄聪明才智, 慎用C++的一切特性,继承、虚函数、操作符重载、模板、异常、new delete、……,更加不要用它们创造出什么奇技淫巧,必须用它们的时候,必须要有使用它们的理由。确实存在必须使用它们的理由,还坚决不用,那就是傻瓜和偏执狂了,这不是合格的C++码农,C++虽然不喜欢胡作非为的捣蛋鬼,但也杜绝一切墨守成规的书呆子。

posted @ 2011-07-11 09:29 华夏之火 阅读(2764) | 评论 (30)编辑 收藏

仅列出标题
共5页: 1 2 3 4 5 

导航

统计

常用链接

留言簿(6)

随笔分类

随笔档案

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜