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

            Shuffy

            不斷的學(xué)習(xí),不斷的思考,才能不斷的進(jìn)步.Let's do better together!
            posts - 102, comments - 43, trackbacks - 0, articles - 19

            [轉(zhuǎn)]http://www.shnenglu.com/tiandejian/archive/2007/06/01/ec_20.html
            第20條:
               
            盡量使用“引用常量”傳參,而不是傳值

            默認(rèn)情況下, C++ 為函數(shù)傳入和傳出對(duì)象是采用傳值方式的(這是由 C 語(yǔ)言繼承而來(lái)的特征)。除非你明確使用其他方法,函數(shù)的形式參數(shù)總會(huì)通過(guò)復(fù)制實(shí)在參數(shù)的副本來(lái)創(chuàng)建,并且,函數(shù)的調(diào)用者得到的也是函數(shù)返回值得一個(gè)副本。這些副本是由對(duì)象的拷貝構(gòu)造函數(shù)創(chuàng)建的。這使得“傳值”成為一項(xiàng)代價(jià)十分昂貴的操作。請(qǐng)觀察下邊的示例中類的層次結(jié)構(gòu):

            class Person {

            public:

             Person();                      // 為簡(jiǎn)化代碼省略參數(shù)表

             virtual ~Person();             // 7 條解釋了它為什么是虛函數(shù)

             ...

             

            private:

             std::string name;

             std::string address;

            };

             

            class Student: public Person {

            public:

             Student();                     // 再次省略參數(shù)表

             virtual ~Student();

             ...

             

            private:

             std::string schoolName;

             std::string schoolAddress;

            };

            請(qǐng)觀察下面的代碼,這里我們調(diào)用一個(gè)名為 validateStudent 函數(shù),通過(guò)為這一函數(shù)傳進(jìn)一個(gè) Student 類型的參數(shù)(傳值方式),它將返回這一學(xué)生的身份是否合法:

            bool validateStudent(Student s);// 通過(guò)傳值方式接受一個(gè) Student 對(duì)象

             

            Student plato;                  // 柏拉圖是蘇格拉底的學(xué)生

             

             

            bool platoIsOK = (plato);   // 調(diào)用這一函數(shù)

            在這個(gè)函數(shù)被調(diào)用時(shí)將會(huì)發(fā)生些什么呢?

            很顯然地,在這一時(shí)刻,通過(guò)調(diào)用 Student 的拷貝構(gòu)造函數(shù),可以將這一函數(shù) s 參數(shù)初始化為 plato 的值 。同樣顯然的是, s validateStudent 返回的時(shí)候?qū)⒈讳N毀。所以這一函數(shù)中傳參的開(kāi)銷就是調(diào)用一次 Student 的拷貝構(gòu)造函數(shù)和一次 Student 的析構(gòu)函數(shù)。

            但是上邊的分析僅僅是冰山一角。一個(gè) Student 對(duì)象包含兩個(gè) string 對(duì)象,所以每當(dāng)你構(gòu)造一個(gè) Student 對(duì)象時(shí),你都必須構(gòu)造兩個(gè) string 對(duì)象。同時(shí),由于 Student 類是從 Person 類繼承而來(lái),所以在每次構(gòu)造 Student 對(duì)象時(shí),你都必須再構(gòu)造一個(gè) Person 對(duì)象。一個(gè) Person 對(duì)象又包含兩個(gè)額外的 string 對(duì)象,所以每次對(duì) Person 的構(gòu)造還要進(jìn)行額外的兩次 string 的構(gòu)造。最后的結(jié)果是,通過(guò)傳值方式傳遞一個(gè) Student 對(duì)象會(huì)引入以下幾個(gè)操作:調(diào)用一次 Student 的拷貝構(gòu)造函數(shù),調(diào)用一次 Person 的拷貝構(gòu)造函數(shù),調(diào)用四次 string 的拷貝構(gòu)造函數(shù)。在 Student 的這一副本被銷毀時(shí),相應(yīng)的每次構(gòu)造函數(shù)調(diào)用都對(duì)應(yīng)著一次析構(gòu)函數(shù)的調(diào)用。因此我們看到:通過(guò)傳值方式傳遞一個(gè) Student 對(duì)象總體的開(kāi)銷究竟有多大?調(diào)用六次構(gòu)造函數(shù)和六次析構(gòu)函數(shù)!

            下面向你介紹正確的方法,這一方法才會(huì)使函數(shù)擁有期望的行為。畢竟你期望的是所有對(duì)象以可靠的方式進(jìn)行初始化和銷毀。然而,如果可以繞過(guò)所有這些構(gòu)造函數(shù)和析構(gòu)函數(shù)將是件很愜意的事情。那就是:通過(guò)引用常量傳遞參數(shù):

            bool (const Student& s);

            這樣做效率會(huì)提高很多:由于不會(huì)創(chuàng)建新的對(duì)象,所以就不會(huì)存在構(gòu)造函數(shù)或析構(gòu)函數(shù)的調(diào)用。改進(jìn)的參數(shù)表中的 const 是十分重要的。由于早先版本的 validateStudent 通過(guò)傳值方式接收 Student 參數(shù),所以調(diào)用者了解:無(wú)論函數(shù)對(duì)于傳入的 Student 對(duì)象進(jìn)行什么樣的操作,都不會(huì)對(duì)原對(duì)象造成任何影響, validateStudent 僅僅會(huì)對(duì)對(duì)象的副本進(jìn)行修改。而改進(jìn)版本中 Student 對(duì)象是以引用形式傳入的,有必要將其聲明為 const 的,因?yàn)槿绻贿@樣,調(diào)用者就需要關(guān)心傳入 validateStudent Student 對(duì)象有可能會(huì)被修改。

            通過(guò)引用傳參也可以避免“截?cái)鄦?wèn)題”。當(dāng)一個(gè)派生類的對(duì)象以一個(gè)基類對(duì)象的形式傳遞(傳值方式)時(shí),基類的拷貝構(gòu)造函數(shù)就會(huì)被調(diào)用,此時(shí),這一對(duì)象的獨(dú)有特征——使它區(qū)別于基類對(duì)象的特征會(huì)被“截掉”。剩下的只是一個(gè)簡(jiǎn)單的基類對(duì)象,這并不奇怪,因?yàn)樗怯苫悩?gòu)造函數(shù)創(chuàng)建的。這肯定不是你想要的。請(qǐng)看下邊的示例,假設(shè)你正在使用一組類來(lái)實(shí)現(xiàn)一個(gè)圖形視窗系統(tǒng):

            class Window {

            public:

             ...

             std::string name() const;           // return name of window

             virtual void display() const;       // draw window and contents

            };

             

            class WindowWithScrollBars: public Window {

            public:

             ...

             virtual void display() const;

            };

            所有的 Window 對(duì)象都有一個(gè)名字,可以通過(guò) name 函數(shù)取得這個(gè)名字,所有的視窗可以被顯示出來(lái),可以通過(guò)調(diào)用 display 實(shí)現(xiàn)。 display 是虛函數(shù),這一點(diǎn)告訴你簡(jiǎn)單基類 Window 的對(duì)象與派生出的 WindowWithScrollBars 對(duì)象的顯示方式是不一樣的。(參見(jiàn)第 34 36 條)

            現(xiàn)在,假設(shè)你期望編寫一個(gè)函數(shù)來(lái)打印出當(dāng)前窗口的名字然后顯示這一窗口。下面是錯(cuò)誤的實(shí)現(xiàn)方法:

            void printNameAndDisplay(Window w)// 錯(cuò)誤 ! 參數(shù)傳遞的對(duì)象將被截?cái)唷?/span>

            {

             std::cout << w.name();

             w.display();

            }

            考慮一下當(dāng)你將一個(gè) WindowWithScrollBars 對(duì)象傳入這個(gè)函數(shù)時(shí)將會(huì)發(fā)生些什么:

            WindowWithScrollBars wwsb;

             

            printNameAndDisplay(wwsb);

            參數(shù) w 將被構(gòu)造——還記得么?它是通過(guò)傳值方式傳入的——就像一個(gè) Window 對(duì)象,使 wwsb 具體化的獨(dú)有信息將被截掉。無(wú)論傳入函數(shù)的對(duì)象的具體類型是什么,在 printNameAndDisplay 的內(nèi)部, w 將總保有一個(gè) Window 類的對(duì)象的身份(因?yàn)樗旧砭褪且粋€(gè) Window 的對(duì)象)。特別地,在 printNameAndDisplay 內(nèi)部對(duì) display 的調(diào)用總會(huì) Window::display ,而永遠(yuǎn)不會(huì)是 WindowWithScrollBars::display

            解決截?cái)鄦?wèn)題的方法是:通過(guò)引用常量傳參:

            void printNameAndDisplay(const Window& w)

            {                               // 工作正常,參數(shù)將不會(huì)被截?cái)唷?/span>

             std::cout << w.name();

             w.display();

            }

            現(xiàn)在 w 的類型就是傳入視窗對(duì)象的精確類型。

            揭開(kāi) C++ 編譯器的面紗,你將會(huì)發(fā)現(xiàn)引用通常情況下是以指針的形式實(shí)現(xiàn)的,所以通過(guò)引用傳遞通常意味著實(shí)際上是在傳遞一個(gè)指針。因此,如果傳遞一個(gè)內(nèi)建數(shù)據(jù)類型的對(duì)象(比如 int ),傳值會(huì)被傳遞引用更為高效。那么,對(duì)于內(nèi)建數(shù)據(jù)類型,當(dāng)你在傳值和傳遞常量引用之間徘徊時(shí),傳值方式不失為一個(gè)更好的選擇。迭代器 STL 中的函數(shù)對(duì)象都是如此,這是因?yàn)樗鼈冊(cè)O(shè)計(jì)的初衷就是更適于傳值,這是 C++ 的慣例。實(shí)現(xiàn)迭代器和函數(shù)對(duì)象的人員有責(zé)任考慮復(fù)制時(shí)的效率問(wèn)題和截?cái)鄦?wèn)題。(這就是一個(gè)“使用哪種規(guī)則,取決于當(dāng)前使用哪一部份的 C++ ”,參見(jiàn)第 1 條)

            內(nèi)建數(shù)據(jù)類型體積較小,所以一些人得出這樣的結(jié)論:所有體積較小的類型都適合使用傳值,即使它們是用戶自定義的。這是一個(gè)不可靠的推理。僅僅通過(guò)一個(gè)對(duì)象體積小并不能判定調(diào)用它的拷貝構(gòu)造函數(shù)的代價(jià)就很低。許多對(duì)象——包括大多數(shù) STL 容器——其中僅僅包含一個(gè)指針和很少量的其它內(nèi)容,但是復(fù)制這樣的對(duì)象的同時(shí),它所指向的所有內(nèi)容都需要復(fù)制。這將會(huì)是一件十分昂貴的事情。

            即使體積較小的對(duì)象的拷貝構(gòu)造函數(shù)不會(huì)帶來(lái)昂貴的開(kāi)銷,它也會(huì)引入性能問(wèn)題。一些編譯器對(duì)內(nèi)建數(shù)據(jù)類型和用戶自定義數(shù)據(jù)類型是分別對(duì)待的,即使它們的原始表示方式完全相同。比如說(shuō)一些編譯器很樂(lè)意將一個(gè)單純的 double 值放入寄存器中,這是語(yǔ)言的常規(guī),但將僅包含一個(gè) double 值的對(duì)象放入寄存器時(shí),編譯器就會(huì)報(bào)錯(cuò)了。當(dāng)你遇到這種事情時(shí),你可以使用引用傳遞這類對(duì)象,因?yàn)榫幾g器此時(shí)一定會(huì)將指針(引用的具體實(shí)現(xiàn))放入寄存器中。

            小型用戶自定義數(shù)據(jù)類型不適用于傳值方式還有一個(gè)理由,那就是:作為用戶自定義類型,它們的大小并不是固定的。現(xiàn)在很小的類型在未來(lái)的版本中可能會(huì)變得很大,這是因?yàn)樗膬?nèi)部實(shí)現(xiàn)方式可能會(huì)改變。即使是你更改了 C++ 語(yǔ)言的具體實(shí)現(xiàn)都可能會(huì)影響到類型的大小。比如,在我編寫上面的示例的時(shí)候,一些對(duì)標(biāo)準(zhǔn)庫(kù)中 string 實(shí)現(xiàn)的大小竟然達(dá)到了另一些的七倍

            總體上講,只有內(nèi)建數(shù)據(jù)類型、 STL 迭代器和函數(shù)對(duì)象類型適用于傳值方式。對(duì)于所有其它的類型,都應(yīng)該遵循本條款中的建議:使用引用常量傳參,而不是傳值。

            牢記在心

            盡量使用引用常量傳參,而不是傳值方式。因?yàn)閭饕酶咝В铱梢员苊?#8220;截?cái)鄦?wèn)題”。

            對(duì)于內(nèi)建數(shù)據(jù)類型、 STL 迭代和函數(shù)對(duì)象類型,這一規(guī)則就不適用了,對(duì)它們來(lái)說(shuō)通常傳值方式更實(shí)用。

            久久亚洲国产精品一区二区| 91久久精品视频| 久久久久久久波多野结衣高潮| www亚洲欲色成人久久精品| 亚洲一区中文字幕久久| 婷婷久久综合| 亚洲精品高清久久| 亚洲一区精品伊人久久伊人| 久久99国内精品自在现线| 久久WWW免费人成—看片| 亚洲综合日韩久久成人AV| 色偷偷888欧美精品久久久| 国产99久久久国产精品小说| 久久国产精品-久久精品| 亚洲欧美一区二区三区久久| 丁香五月网久久综合| 午夜视频久久久久一区| 久久综合九色综合97_久久久| 国产欧美久久久精品影院| 久久电影网一区| 久久精品无码专区免费青青| 一97日本道伊人久久综合影院| 国产精品久久久久9999| 亚洲AV无一区二区三区久久| 日本国产精品久久| 国产精品va久久久久久久| 久久青青草原亚洲av无码app | 精品久久久久久无码中文字幕一区| 久久精品国产国产精品四凭| 久久综合九色综合精品| 久久久精品人妻一区二区三区蜜桃| 欧美一区二区久久精品| 亚洲国产小视频精品久久久三级| AA级片免费看视频久久| 国产精品热久久毛片| 国产叼嘿久久精品久久| 品成人欧美大片久久国产欧美...| 久久婷婷国产综合精品| 99久久这里只有精品| 99久久精品九九亚洲精品| 国产精品日韩深夜福利久久|