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

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

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

            本篇摘要

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

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

            準(zhǔn)備工作

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

            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類的兩個(gè)對(duì)象和兩個(gè)指針,定義如下:

            Person youngMan(18,” young man”);

            Person oldMan(81,” old man”);

            Person* pYoungMan = &youngMan;

            Person* pOldMan = &oldMan;

            最常見的交換兩個(gè)對(duì)象的方法:GeneralSwap

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

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

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

            利用GeneralSwap交換兩個(gè)用戶自定義對(duì)象

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

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

            GeneralSwap(pYoungMan,pOldMan);

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

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

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

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

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

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

            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可以用于交換兩個(gè)整數(shù),但由于涉及加減法,因此有數(shù)據(jù)溢出的危險(xiǎn);也可以用于交換浮點(diǎn)數(shù),但是有可能由于舍入誤差導(dǎo)致結(jié)果不準(zhǔn)確。

            Add_Sub_Swap_1不能用于交換兩個(gè)用戶自定義的對(duì)象,下面的語(yǔ)句編譯就通過不,編譯器告訴你Person類沒有定義operator +等符號(hào):

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

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

            利用加減法交換兩個(gè)指針

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

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

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

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

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

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

                上面的函數(shù)的實(shí)用性非常有限,它只能交換兩個(gè)整數(shù)(包含char,int,long),要想交換兩個(gè)浮點(diǎn)數(shù)是不行的,因?yàn)楦↑c(diǎn)數(shù)不能參與位運(yùn)算,要想交換兩個(gè)指針也是不行的,編譯器不允許你把兩個(gè)指針拿來(lái)做位運(yùn)算,要想交換兩個(gè)用戶自定義對(duì)象也是不行的,因?yàn)樗匀徊荒軈⑴c位運(yùn)算。那么是不是利用異或交換兩個(gè)變量就沒法用于浮點(diǎn)數(shù)、指針和用戶自定義的對(duì)象了呢?答案是“能”!后面幾節(jié)我將闡述這些問題。

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

                前面的Xor_Swap_1無(wú)法實(shí)現(xiàn)兩個(gè)浮點(diǎn)數(shù)和指針的交換,其原因是浮點(diǎn)數(shù)和指針均不直接支持位運(yùn)算。那么如何才能利用異或來(lái)交換兩個(gè)浮點(diǎn)數(shù)和指針呢?方法仍然是“強(qiáng)制類型轉(zhuǎn)換”!因?yàn)楦↑c(diǎn)數(shù)在內(nèi)存中仍然是用一串二進(jìn)制bit來(lái)表示的嘛,只要把浮點(diǎn)數(shù)看作(強(qiáng)制類型轉(zhuǎn)換)二進(jìn)制bit構(gòu)成的整數(shù),那么就能進(jìn)行位運(yùn)算了,至于指針嘛,處理方法完全相同。具體如何做呢,其實(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));
            }

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

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

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

            這里我要深入分析一下強(qiáng)制類型轉(zhuǎn)換是如何發(fā)生位截?cái)嗟模紫瓤纯匆韵聹y(cè)試的輸出結(jié)果,注意代碼中的注釋,為了節(jié)約篇幅,我把值得注意的地方都放在注釋中了:

            Double a = 1.0,b=2.0;

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

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

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

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

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

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

            既然我們知道了Xor_Swap_2為什么不能用于交換兩個(gè)double類型的數(shù)據(jù)和兩個(gè)用戶自定義的數(shù)據(jù),那么就有辦法對(duì)它進(jìn)行改進(jìn)。具體改進(jìn)的思想就是把參數(shù)按照一個(gè)byte一個(gè)byte地分別異或,按照這個(gè)思路我實(shí)現(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));
                     }
            }

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

            結(jié)束語(yǔ)

                本篇到此寫完了,有種不好的感覺,因?yàn)槲闹写罅渴褂?#8220;強(qiáng)制類型轉(zhuǎn)換”而這個(gè)東西是C++中容易出錯(cuò)的地方,而我再寫本文時(shí),并沒有去復(fù)習(xí)關(guān)于強(qiáng)制類型轉(zhuǎn)換的相關(guān)知識(shí),因此擔(dān)心很多地方有潛在的出錯(cuò)可能,還請(qǐng)各位磚家指正!

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

            Feedback

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

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

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

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

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

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

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

            2011-08-11 13:33 by right
            雖然這種探索的意義不大,樓主的探索精神還是值得表?yè)P(yáng)的。
            提一個(gè)函數(shù),我沒仔細(xì)看,可能樓主用的上
            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;
            }

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


            国产精品中文久久久久久久| 九九久久99综合一区二区| 久久黄视频| 久久精品人妻中文系列| 色综合久久久久无码专区| 91精品国产91久久久久福利| 国产成人久久精品二区三区| 一本久久综合亚洲鲁鲁五月天亚洲欧美一区二区 | 久久国产亚洲精品麻豆| 久久免费视频6| 无码AV波多野结衣久久| 久久国产香蕉视频| 精品国产一区二区三区久久久狼| 久久精品亚洲乱码伦伦中文| 久久综合狠狠综合久久| 四虎影视久久久免费| 香蕉久久夜色精品国产小说| 日产精品99久久久久久| 久久青青草视频| 综合久久精品色| 精品久久久久久无码人妻蜜桃| 国内精品九九久久久精品| 九九精品久久久久久噜噜| 免费一级做a爰片久久毛片潮| 久久精品国内一区二区三区 | 精品久久亚洲中文无码| 国産精品久久久久久久| 曰曰摸天天摸人人看久久久| 成人国内精品久久久久一区| 亚洲女久久久噜噜噜熟女| 久久婷婷人人澡人人爽人人爱| 久久影院午夜理论片无码| 久久久久国色AV免费观看| 精品免费久久久久国产一区| 日韩亚洲欧美久久久www综合网 | 国产精品久久久久影院色| 精品久久久久久国产潘金莲| 久久国产亚洲精品麻豆| 狠狠精品干练久久久无码中文字幕| 99久久国产主播综合精品| 久久天天日天天操综合伊人av|