• <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>

            huaxiazhihuo

             

            有理數類的一點思考

                    在寫24點的程序時,要處理除法運算,不可避免地將引入小數點,但本人對于計算機中的浮點數實在沒有太多的好感。權衡再三,終于決定下定決心寫一個有理數類(以下簡稱為CRational),以解決除法運算這個問題。并且有理數這個類也是一個很好的C++練習題,可以用來練練手,以下將看到,它的實現雖然不難,但也不是很容易。有理數這個東西,屬于用戶自定義的數據類型,c++對此的支持,真可謂完美,既不失效率,又具備美觀。c++可以讓程序員做出來的自定義類型,其行為可以表現得好像是由語言層面實現的那個樣子,不管從語法、安全、效率上講。這一點,所有的語言都沒法和c++媲美。
                    不管怎么說,CRational的需求相當明確,它一定有分子、分母、然后支持加減乘除這四種運算,于是,一口氣馬上就能寫下它的定義。
            class CRational
            {
            public:
                CRational(
            int nNumberator=0int nDenominator=1);
                
            int Numberator()const return m_nNum;}
                
            int Denominator()const return m_nDe;}

                CRational
            & operator+=(const CRational& _Right);
                CRational
            & operator-=(const CRational& _Right);
                CRational
            & operator*=(const CRational& _Right);
                CRational
            & operator/=(const CRational& _Right);
                CRational 
            operator-()const    // 一元操作符,用以取反
                {
                    
            return CRational(-m_nNum, m_nDe);
                }


            private:
                
            int m_nNum;
                
            int m_nDe;
            }
            ;
                     嗯,我承認代碼很匈牙利,中MFC的毒太深。毋庸置疑,分子分母只能獲取,不能設置,函數名中,沒有加Get的前綴,實在是因為代碼中它出現的地方太多了,所以能避免就盡量避免。沒有涉及資源分配,析構函數可以忽略,拷貝構造函數和賦值函數也不用寫了,編譯器將會提供缺省的實現,足以滿足我們的要求。貌似應該還要有一個返回求取小數值的操作,但是,這個類本身就是為了避免操作小數點,而且,計算小數點值可通過分子/分母的方法計算出來。總之,CRational的終極接口就是這個樣子了。
                    每個有理數都有一個標準的等價類,這個標準的有理數的分子、分母都不能再約分了,而且可以暫時假設符號位出現于分子中,分母則為正整數,比如說,3/6、-4/-8都等價于1/2,因此,在這個有理數類中,必須有一個標準化的操作,問題是,這個標準化函數是返回一個標準的有理數還是將有理數自身直接就標準化了。經過多方面的權衡,特別是為了兼容現有的整型變量(int, char, short等),整型變量可看成分母為1的有理數,我決定讓CRational一直處于標準化的狀態下,標準化的狀態由CRational自己來維持,客戶無須知道標準化的這個細節,因此,將void standarlize()聲明于其private的區域下,其實現如下:
            void CRational::standarlize()
            {
                
            if (m_nDe < 0)
                
            {
                    m_nDe 
            = -m_nDe;
                    m_nNum 
            = -m_nNum;
                }

                
            int nGcd = gcd(abs(m_nNum), m_nDe);
                m_nNum 
            /= nGcd;
                m_nDe 
            /= nGcd;
            }

                    其中,毫無疑問,gcd為最大公約數,gcd沒有訪問CRational的任何非靜態(nonstatic)變量,因此,它必將不可成為CRational的非靜態函數。將gcd聲明為靜態函數,雖然可避免污染全局空間,但也使得外部代碼必須通過CRational來調用gcd函數,語法上不方便,而且,gcd本身就是一個獨立性很強而且又通用的函數,從意義上,它也不應該屬于CRational里面的東西。因此,gcd只能為全局函數。關于靜態函數和全局函數的選擇,鑒于“類的接口要盡可能的小”的原則和其他的一些問題,只要函數不訪問類的靜態變量,也不通過類的變量來訪問到其非靜態成員,它就應該是全局函數。全局函數是好東西,某些語言為了堅持所謂的純粹的面向對象,故意不支持,實在讓人用起來很不痛快,它的污染全局空間的問題,完全可以通過命名空間來解決。總之,只要可能的話,就應該將函數聲明為全局函數,沒什么不好。好了,請看gcd的實現。
            unsigned int gcd(unsigned int x, unsigned int y)   
            {   
                unsigned  
            int  nTimes=0;   
                
            for (; 0 == (x&1&& 0 == (y&1); x>>=1, y>>=1)
                    
            ++nTimes;

                
            if (x < y)
                    swap(x, y);

                
            while (y > 0)
                
            {
                    
            for (; 0 == (x & 1 );x >>= 1 )
                        ;   

                    
            if (x < y)
                        swap(x, y);
                    x 
            -= y;
                    
            if (x < y)
                        swap(x, y);
                }

                
            return x << nTimes;
            }
             
                    其算法源于《編程之美》,可看成是非遞歸的版本。咦,怎么會這么長,與日常所見的到似乎不太一樣,高效算法的代碼貌似都會很長,好比strlen。再仔細看,里面居然沒有取余的操作。嗯,它的算法核心,用移位和減法這兩種快速的運算來替代取模這種慢速運算。
            有了standarlize()之后,CRational的幾個函數的實現如下所示:
            CRational::CRational(int nNumberator, int nDenominator)
            : m_nNum(nNumberator), m_nDe(nDenominator)
            {
                assert(nDenominator 
            != 0);
                standarlize();
            }


            CRational
            & CRational::operator+=(const CRational& _Right)
            {
                m_nNum 
            = m_nNum*_Right.m_nDe + _Right.m_nNum*m_nDe;
                m_nDe 
            *= _Right.m_nDe;
                standarlize();
                
            return *this;
            }
            ……
                    構造函數中,似乎應該檢查分母為0的情況,然后拋出異常。但是,這屬于契約使用的問題,用戶違背的契約,一切后果,必須自己承擔,我們的代碼無須對此負責。
                    此外,就是各種+、-、*、/、==、輸出等各種全局運算符重載的操作了。得益于C++的缺省類型轉換,我們不用再做其他事情,就可以很好地讓我們的CRational很好地與融入到原有的各種整型世界中去。當然,為了效率起見,似乎有必要針對各種整型提供+、-、*、/的各種重載版本(complex就是這樣做的),但在此,確實沒有必要。缺省類型轉換有時雖然會帶來一些問題,但是,當確實需要它的時候,它就能發揮重大作用了。C++的各種特性就是這樣,你可以不用,它也不打擾你(你要故意或無意用錯,那也沒辦法),當真正需要到的時候,特性的威力就顯示出來了。很多人之所以愿意沉迷于C++,就在于它不剝奪程序員的任何一點選擇的權利。
            ostream& operator << (ostream& outconst CRational& rat)
            {
                cout 
            << rat.Numberator();
                
            if (rat.Denominator() != 1)
                    cout 
            <<  "/" << rat.Denominator();
                
            return out;
            }


            CRational 
            operator+(const CRational& _Left, const CRational& _Right)
            {
                CRational _Tmp(_Left);
                
            return _Tmp += _Right;
            }
            ……
                    嗯,一再寫這些入門級的文章,本座也很自覺有失身價,也算是對網絡世界的一點回報吧。只要有一個人看了,能有所啟發,在下就心滿意足了,為免誤人子弟,也歡迎有人批評指正。

            posted on 2012-06-04 11:23 華夏之火 閱讀(1404) 評論(0)  編輯 收藏 引用

            導航

            統計

            常用鏈接

            留言簿(6)

            隨筆分類

            隨筆檔案

            搜索

            積分與排名

            最新評論

            閱讀排行榜

            評論排行榜

            av午夜福利一片免费看久久| 久久精品中文字幕一区| 一级A毛片免费观看久久精品| 久久亚洲精品中文字幕三区| 国产一区二区精品久久| 久久国产精品成人片免费| 亚洲成色WWW久久网站| 国产亚洲精品久久久久秋霞| 久久亚洲AV成人无码| 久久天天躁狠狠躁夜夜躁2014| 热久久视久久精品18| 思思久久精品在热线热| 日韩av无码久久精品免费| 久久久久久久久久久久中文字幕 | 亚洲av伊人久久综合密臀性色| 久久午夜福利无码1000合集| 亚洲国产美女精品久久久久∴| 潮喷大喷水系列无码久久精品| 亚洲国产精品婷婷久久| 亚洲精品成人久久久| 欧美黑人又粗又大久久久| 国产精品视频久久| 欧美久久一区二区三区| 成人久久免费网站| 久久久久四虎国产精品| 中文字幕精品久久久久人妻| 2022年国产精品久久久久 | 亚洲欧美另类日本久久国产真实乱对白 | 亚洲综合精品香蕉久久网| 国产V亚洲V天堂无码久久久| 久久国产成人午夜AV影院| 免费无码国产欧美久久18| 久久99热狠狠色精品一区| 综合久久久久久中文字幕亚洲国产国产综合一区首 | 欧美亚洲国产精品久久久久| 久久永久免费人妻精品下载| 久久精品这里只有精99品| 精品久久久久久无码中文字幕一区 | 久久人人青草97香蕉| 狠狠色噜噜狠狠狠狠狠色综合久久| 日韩美女18网站久久精品|