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

            堅持學(xué)習(xí)/暴露問題/不斷提升

            c++/設(shè)計模式/算法結(jié)構(gòu)/系統(tǒng)
            posts - 2, comments - 20, trackbacks - 0, articles - 0

            本篇摘要

              交換兩個變量是非常古老的話題了,然而本文絕對保證給你新鮮的感覺!本文涉及到最簡單的“不用臨時變量交換兩個整數(shù)”還涉及到如果利用異或來實現(xiàn)兩個指針、兩個浮點數(shù)的交換,要知道指針的浮點數(shù)是不允許直接異或運算的哦;同時本文還闡述了如何交換用戶自定義類型及其指針。

            本文完全是個人自由發(fā)揮之作,歡迎廣大磚家來拍磚,我個人感覺文中必然有很多不足,甚至錯誤之處,這并非我謙虛,事實上我寫本文的目的就是希望在挨磚板中成長!新人或許看不懂很多東西,如果你看不懂,那么就不要隨便膜拜,因為看不懂的或許原本就是錯的,高手看完后如果本文寫得還可以,那么請留下好評,以供新手參考本文是否有閱讀本文的必要,如果覺得本文完全是垃圾,那么請不要客氣,您可以赤裸地,露骨地指出本文的錯誤和不足,讓本人在批評中進步,但請不要進行人身攻擊,謝謝!

            準備工作

              由于本文涉及到交換兩個用戶自定義類型的變量,為了舉例方便,本文定義如下的Person類(其中省略了拷貝構(gòu)造函數(shù)的重寫,因為本文不用到它):

            class Person
            {
            public:
                     Person(
            int age ,const char* name ):m_Age(age)
                     {
                               
            int len = strlen(name);
                               
            this->m_Name = new char[len+1];
                               strcpy(
            this->m_Name,name);
                     }
                     Person()
                     {
                               
            this->m_Age = -1;
                               
            this->m_Name = 0;
                     }
                     
            void PrintSelf()
                     {
                               cout
            <<this->m_Name<<":"<<this->m_Age<<endl;
                     }
                     Person
            & operator= (const Person& other)
                     {
                               
            if (this == &other)
                               {
                                        
            return *this;
                               }
                               
            else
                               {
                                        
            this->m_Age = other.m_Age;
                                        delete 
            this->m_Name;
                                        
            int len = strlen(other.m_Name);
                                        
            this->m_Name = new char[len+1];
                                        strcpy(
            this->m_Name,other.m_Name);
                                        
            return *this;
                               }
                     }
                     
            ~Person()
                     {
                               delete 
            this->m_Name;
                     }
            private:
                     
            int m_Age;
                     
            char* m_Name;
            };

              為了后文表述方便,這里再定義Person類的兩個對象和兩個指針,定義如下:

            Person youngMan(18,” young man”);

            Person oldMan(81,” old man”);

            Person* pYoungMan = &youngMan;

            Person* pOldMan = &oldMan;

            最常見的交換兩個對象的方法:GeneralSwap

            通常,我們?yōu)榱私粨Q兩個變量都采取下面的方法來實現(xiàn),它需要一個臨時變量:

            template<class T>
            void GeneralSwap(T& a,T& b)
            {
                     T temp;
                     temp 
            = a;
                     a 
            = b;
                     b 
            = temp;
            }

                顯然人人都知道這個寫法,但是我仍然覺得有必要重點申明幾點:1、注意函數(shù)的參數(shù)是引用(也可指針),為什么我就不解釋了;2、這個交換函數(shù)基本上是最簡單、最通用的,簡單到人人都會寫,通用到它幾乎可適用于任何數(shù)據(jù)類型:char , int , long , float, double等各種系統(tǒng)自定義數(shù)學(xué)類型(無符號的,帶符號的),用戶自定義數(shù)據(jù)類型(需要有默認構(gòu)造函數(shù),否則語句T temp;會報錯),以及各種指針(系統(tǒng)自定義類型的指針,和用戶自定義類型的指針)。當(dāng)然用戶自定義類型中如果包含了指針數(shù)據(jù)成員,那么需要重載賦值運算符,事實上這樣的用戶自定義類,你都應(yīng)該自己重寫賦值運算符、拷貝構(gòu)造函數(shù),否則不但不能使用GeneralSwap,其他涉及到拷貝和賦值的操作都可能導(dǎo)致出錯!

            利用GeneralSwap交換兩個用戶自定義對象

                下面深入探討一下關(guān)于用戶自定義對象的交換問題:針對準備工作中的Person類的兩個對象youngMan和oldMan語句GeneralSwap(youngMan,oldMan);能實現(xiàn)他們的交換。短短一行代碼就能實現(xiàn)將一個18歲的花季少男跟一個81歲的老頭子掉包,這像不像是耍魔術(shù)啊,呵呵。要注意了,該交換代碼雖短,但涉及到默認構(gòu)造函數(shù)的調(diào)用(GeneralSwap中的T temp;語句)和賦值運算符重載函數(shù)的調(diào)用(GeneralSwap中的三個賦值語句)。

            或許您很少這么用吧,事實上在我寫本文之前,我都沒真正交換過兩個自定義的對象,通常我們都不愿意這么交換兩個自定義對象。原因是效率太低!或許你要問,萬一有的應(yīng)用就是需要交換兩個自定義的對象怎么辦?好辦,用指針啊!對,指針的好處就是效率高,為什么C++比java效率高,原因之一就是java取消了指針。下面的第一行代碼就是交換兩個Person類的指針:

            GeneralSwap(pYoungMan,pOldMan);

            //GeneralSwap(*pYoungMan,* pOldMan);     //效率低

                為什么使用指針就效率高了呢?原因是指針就是地址,地址就是整數(shù),于是問題等價于交換兩個整數(shù),因此它不調(diào)用賦值運算符重載函數(shù)!只要你在應(yīng)用程序中始終通過指向?qū)ο蟮闹羔榿碓L問對象,那么交換兩個指針就能達到交換對象的目的。注意被注釋掉的第二行代碼,它是正確的,但是它又回到了交換兩個實際對象,其效率低,最好不要這么用!

            對于這個最常見、最簡單的GeneralSwap我都廢話了一大堆,而且還扯出了一個沒多少用的關(guān)于用戶自定義對象的交換問題,這實屬個人思維散射,請磚家們狠狠地拍。

            在進行下一個方法之前,再次強調(diào)一點,這個方法的特點是簡單、通用!后面的方法都將與之做比較。

            利用加減法實現(xiàn)兩個數(shù)的交換

                幾乎人人都知道還可以利用加減法來實現(xiàn)兩個數(shù)的交換,其代碼也異常簡單:

            template<class T>
            void Add_Sub_Swap_1(T& a, T& b)
            {
                    a 
            = a+b;
                    b 
            = a-b;
                     a 
            = a-b;
            }

                Add_Sub_Swap_1可以用于交換兩個整數(shù),但由于涉及加減法,因此有數(shù)據(jù)溢出的危險;也可以用于交換浮點數(shù),但是有可能由于舍入誤差導(dǎo)致結(jié)果不準確。

            Add_Sub_Swap_1不能用于交換兩個用戶自定義的對象,下面的語句編譯就通過不,編譯器告訴你Person類沒有定義operator +等符號:

            Add_Sub_Swap_1(youngMan,oldMan);//編譯通不過!

                Add_Sub_Swap_1不能用于交換兩個指針,語句Add_Sub_Swap_1(pYoungMan,pOldMan);編譯時將報錯:error C2110: cannot add two pointers,是的,兩個指針不能直接做加法運算(減法是可以的)。那么是不是就不能利用加減法實現(xiàn)兩個指針的交換呢?答案是:“可以!”,接下來我將闡述如何實現(xiàn)。

            利用加減法交換兩個指針

                Add_Sub_Swap_1不能用于交換兩個指針,前面我說可以用加減法來實現(xiàn)兩個指針的交換,這是有根據(jù)的:指針仍然是變量,只不過它是存儲普通變量的地址的變量。只要我們把指針“看作”變量,那么就能實現(xiàn)加法。那么如何把指針“看作”變量呢?答案是:“通過強制類型轉(zhuǎn)換”!指針表示變量的地址,在32位平臺上它是一個無符號的整數(shù),因此可以將指針強制轉(zhuǎn)換為無符號類型的整數(shù)。我對上面的Add_Sub_Swap_1進行了改進:

            template<class T>
            void Add_Sub_Swap_2(T& a, T& b)
            {
                     
            *(( unsigned*)(&a)) = *(( unsigned*)(&a)) + *(( unsigned*)(&b));
                     
            *(( unsigned*)(&b)) = *(( unsigned*)(&a)) - *(( unsigned*)(&b));
                     
            *(( unsigned*)(&a)) = *(( unsigned*)(&a)) - *(( unsigned*)(&b));
            }

                利用Add_Sub_Swap_2既可以交換兩個普通的整數(shù)、浮點數(shù)同時它可以交換兩個任意類型的指針(包含系統(tǒng)預(yù)定義類型和用戶自定義類型的指針,其實本質(zhì)上所有指針都屬于同一種類型:32位無符號整數(shù)類型)。不信您試試Add_Sub_Swap_2(pYoungMan,pOldMan);它能得到正確答案。

            雖然Add_Sub_Swap_2解決了Add_Sub_Swap_1無法交換兩個指針的問題,但是它仍然無法交換兩個用戶自定義類型的變量,原因是用戶自定義類型沒有加減法運算。看來要想用加減法實現(xiàn)兩個用戶定義類型的交換是不可能的了(除非用戶自定義的operator+和operator-能滿足交換兩個對象的目的,這很難,除非是非常簡單的用戶自定義類型,比如你不使用系統(tǒng)類型int非要定義一個MyInt類)。

            利用異或?qū)崿F(xiàn)兩個整數(shù)的交換

                同樣地,幾乎人人都知道利用異或來交換兩個數(shù),其實現(xiàn)也非常簡單:

            template <class T>
            void Xor_Swap_1(T& a,T& b)
            {
                     a 
            = a^b;
                     b 
            = a^b;
                     a 
            = a^b;
            }

                上面的函數(shù)的實用性非常有限,它只能交換兩個整數(shù)(包含char,int,long),要想交換兩個浮點數(shù)是不行的,因為浮點數(shù)不能參與位運算,要想交換兩個指針也是不行的,編譯器不允許你把兩個指針拿來做位運算,要想交換兩個用戶自定義對象也是不行的,因為它仍然不能參與位運算。那么是不是利用異或交換兩個變量就沒法用于浮點數(shù)、指針和用戶自定義的對象了呢?答案是“能”!后面幾節(jié)我將闡述這些問題。

            利用異或?qū)崿F(xiàn)兩個float和指針的交換

                前面的Xor_Swap_1無法實現(xiàn)兩個浮點數(shù)和指針的交換,其原因是浮點數(shù)和指針均不直接支持位運算。那么如何才能利用異或來交換兩個浮點數(shù)和指針呢?方法仍然是“強制類型轉(zhuǎn)換”!因為浮點數(shù)在內(nèi)存中仍然是用一串二進制bit來表示的嘛,只要把浮點數(shù)看作(強制類型轉(zhuǎn)換)二進制bit構(gòu)成的整數(shù),那么就能進行位運算了,至于指針嘛,處理方法完全相同。具體如何做呢,其實現(xiàn)大概是這樣的:

            template <class T>
            void Xor_Swap_2(T& a,T& b)
            {
                     
            *((unsigned*)(&a)) = *((unsigned*)(&a)) ^ *((unsigned*)(&b));
                     
            *((unsigned*)(&b)) = *((unsigned*)(&a)) ^ *((unsigned*)(&b));
                     
            *((unsigned*)(&a)) = *((unsigned*)(&a)) ^ *((unsigned*)(&b));
            }

                利用這個函數(shù)可以交換兩個float類型的變量,也可以交換任意類型的指針!非常值得注意的是:用它交換兩個double類型數(shù)據(jù)或者兩個Person類的對象(youngMan,oldMan)均能編譯通過,但是其結(jié)果卻是錯的。至于為什么,以及如何解決,這將是我下一節(jié)要闡述的內(nèi)容。

            利用異或?qū)崿F(xiàn)兩個double類型變量和用戶自定義變量的交換

                 Xor_Swap_2解決了利用異或不能交換兩個float數(shù)據(jù)和指針的問題,然而它卻不能正確地交換兩個double數(shù)據(jù)和兩個Person類對象。這是為什么呢?原因是函數(shù)內(nèi)部是把參數(shù)強制類型轉(zhuǎn)換成unsigned類型的,而sizeof(float)和sizeof(pointor)的值都等于sizeof(unsigned),但是sizeof(double)卻不等于sizeof(unsigned),也就是說把double強制轉(zhuǎn)換成unsigned類型時,發(fā)生了“位截斷”(在概念是區(qū)別與數(shù)據(jù)截斷),那么得到的結(jié)果肯定就不對了。至于無法交換兩個Person類對象,其原因也相同。

            這里我要深入分析一下強制類型轉(zhuǎn)換是如何發(fā)生位截斷的,首先看看以下測試的輸出結(jié)果,注意代碼中的注釋,為了節(jié)約篇幅,我把值得注意的地方都放在注釋中了:

            Double a = 1.0,b=2.0;

            Xor_Swap_2(a,b);//交換兩個double數(shù)據(jù)

            Cout<<a<<b;//輸出仍然是1.0和2.0,a,b的值并未改變

            Xor_Swap_2(youngMan,oldMan);//交換兩個用戶自定義對象

            youngMan.PrintSelf();//輸出young man:81

            oldMan.PrintSelf();//輸出old man:18

                可以看出兩個double數(shù)據(jù)并沒被交換,而兩個Person對象在交換之后發(fā)生了怪異現(xiàn)象:產(chǎn)生了81歲的年輕人和18歲的老年人!這一點正好說明強制類型轉(zhuǎn)換時發(fā)生了位截斷,由于Person類的第一個數(shù)據(jù)成員m_Age正好是int型,在Xor_Swap_2內(nèi)部做強制類型轉(zhuǎn)換時正好取得了兩個對象的m_Age成員,于是出現(xiàn)了兩個對象被部分交換的情況,那么又如何解釋兩個double數(shù)據(jù)沒有變法呢?事實上兩個double數(shù)據(jù)仍然發(fā)生了部分交換,因為這里的兩個double數(shù)(a,b)的前4個字節(jié)正好相同,因此看不出部分交換。

            既然我們知道了Xor_Swap_2為什么不能用于交換兩個double類型的數(shù)據(jù)和兩個用戶自定義的數(shù)據(jù),那么就有辦法對它進行改進。具體改進的思想就是把參數(shù)按照一個byte一個byte地分別異或,按照這個思路我實現(xiàn)了如下的函數(shù):

            template <class T>
            void Xor_Swap_3(T& a,T& b)
            {
                     
            int size = sizeof(T);
                     
            for (int i = 0;i<size;i++)
                     {
                               
            *((unsigned char*)(&a)+i) = (*((unsigned char*)(&a)+i)) ^ (*((unsigned char*)(&b)+i));
                               
            *((unsigned char*)(&b)+i) = (*((unsigned char*)(&a)+i)) ^ (*((unsigned char*)(&b)+i));
                               
            *((unsigned char*)(&a)+i) = (*((unsigned char*)(&a)+i)) ^ (*((unsigned char*)(&b)+i));
                     }
            }

                 這個版本的函數(shù)不僅能交換兩個整數(shù)、任何指針、float數(shù)和double數(shù),更牛逼的是它能交換兩個用戶定義類型的變量!事實上它基本上是在內(nèi)存一級上操作數(shù)據(jù),而任何類型的數(shù)據(jù)對象最終都表現(xiàn)為內(nèi)存對象。這其實就是通過內(nèi)存拷貝實現(xiàn)兩個對象的交換的一個版本吧,當(dāng)然還有利用memcpy等手段進行內(nèi)存拷貝來實現(xiàn)兩個變量的交換的,這里我就不贅述了。

            結(jié)束語

                本篇到此寫完了,有種不好的感覺,因為文中大量使用“強制類型轉(zhuǎn)換”而這個東西是C++中容易出錯的地方,而我再寫本文時,并沒有去復(fù)習(xí)關(guān)于強制類型轉(zhuǎn)換的相關(guān)知識,因此擔(dān)心很多地方有潛在的出錯可能,還請各位磚家指正!

            @import url(http://www.shnenglu.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);

            Feedback

            # re: 絕對深入剖析各種方法實現(xiàn)兩個變量的交換[未登錄]  回復(fù)  更多評論   

            2011-08-10 13:11 by Chipset
            這不是4位機和8位機的時代了,這種交換容易出錯。如果兩個數(shù)相同這種交換是不是會出錯?大的數(shù)據(jù)結(jié)構(gòu)交換用move語義,C++0x,此時依賴大量數(shù)據(jù)拷貝的交換永遠快不起來。

            # re: 絕對深入剖析各種方法實現(xiàn)兩個變量的交換  回復(fù)  更多評論   

            2011-08-10 17:22 by 他她女鞋
            好好了解學(xué)習(xí)一下。

            # re: 絕對深入剖析各種方法實現(xiàn)兩個變量的交換  回復(fù)  更多評論   

            2011-08-10 21:33 by 瘋狂的面包
            還是 ^ 操作 本質(zhì)還是玩這個。 我看不到有什么新意。還要注意自己和自己交換。

            # re: 絕對深入剖析各種方法實現(xiàn)兩個變量的交換  回復(fù)  更多評論   

            2011-08-11 13:33 by right
            雖然這種探索的意義不大,樓主的探索精神還是值得表揚的。
            提一個函數(shù),我沒仔細看,可能樓主用的上
            float IntBitsToFloat(int x)
            {
            union
            {
            int n;
            float f;
            } m;
            m.n = x;
            return m.f;
            }
            int FloatToIntBits(float x)
            {
            union
            {
            float f;
            int n;
            } m;
            m.f = x;
            return m.n;
            }

            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            欧美性大战久久久久久| 韩国无遮挡三级久久| 久久这里只精品99re66| 综合久久给合久久狠狠狠97色 | 欧美久久久久久| 性高湖久久久久久久久| 久久99国产精品久久99| 日韩欧美亚洲国产精品字幕久久久| 中文字幕无码久久精品青草 | 婷婷久久五月天| 久久精品成人国产午夜| 亚洲国产精品综合久久一线| 久久久久久亚洲Av无码精品专口| 99久久99久久精品国产| 欧美午夜精品久久久久免费视| 国产成人精品久久亚洲高清不卡| 伊人 久久 精品| 久久精品国产一区二区电影| 久久久国产乱子伦精品作者| 久久久久青草线蕉综合超碰| 狠狠人妻久久久久久综合| 亚洲人成伊人成综合网久久久| 久久久久久免费一区二区三区 | 色综合久久久久| 中文无码久久精品| 久久影视综合亚洲| 色综合合久久天天综合绕视看| 伊人久久久AV老熟妇色| 2021久久精品免费观看| 欧美一级久久久久久久大| 国产精品永久久久久久久久久 | 欧美久久久久久午夜精品| 51久久夜色精品国产| 国产韩国精品一区二区三区久久| 国产成人久久精品一区二区三区| 亚洲欧美成人久久综合中文网 | a高清免费毛片久久| 国产婷婷成人久久Av免费高清| 亚洲va久久久噜噜噜久久天堂| 久久久国产打桩机| 亚洲日韩中文无码久久|