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

            洛譯小筑

            別來無恙,我的老友…
            隨筆 - 45, 文章 - 0, 評論 - 172, 引用 - 0
            數(shù)據(jù)加載中……

            [ECPP讀書筆記 條目12] 要復制整個對象,不要遺漏任一部分

            在一個設(shè)計良好的面向?qū)ο笙到y(tǒng)中,對象的所有內(nèi)在部分都會被封裝起來,只有兩個函數(shù)是用來復制對象的:即拷貝構(gòu)造函數(shù)和拷貝賦值運算符,這兩個函數(shù)的功能恰如其名,我們將它們稱為拷貝函數(shù)。條目5中詳細講述了編譯器將會在必要時自動生成拷貝函數(shù),然后該條目還說明了編譯器生成的版本可以精確的按你所預期的執(zhí)行:當前正在復制的對象的所有數(shù)據(jù)都會得到復制。

            當你聲明你自己的拷貝函數(shù)時,你就向編譯器表明,默認實現(xiàn)方式中有一些內(nèi)容你不喜歡。編譯器會把這種做法視為對它的冒犯,它會以一個古怪的方式來報復你:當你的實現(xiàn)幾乎一定要出現(xiàn)錯誤時,它就是不告訴你。

            下面示例中是一個表示顧客的類,其中拷貝函數(shù)是手動編寫的,以便將對它們的調(diào)用記入日志:

            void logCall(const std::string& funcName);

                                               // 創(chuàng)建一個日志記錄

            class Customer {

            public:

              ...

              Customer(const Customer& rhs);

              Customer& operator=(const Customer& rhs);

              ...

             

            private:

              std::string name;

            };

             

            Customer::Customer(const Customer& rhs)

            : name(rhs.name)                   // 復制rhs中的數(shù)據(jù)

            {

              logCall("Customer copy constructor");

            }

             

            Customer& Customer::operator=(const Customer& rhs)

            {

              logCall("Customer copy assignment operator");

              name = rhs.name;                 // 復制rhs中的數(shù)據(jù)

              return *this;                    // 參見條目10

            }

            這里看上去一切正常,而且實際上一切確實是正常的——但當另一個數(shù)據(jù)成員添加入Customer時,意外就發(fā)生了:

            class Date { ... };                // 記錄日期

             

            class Customer {

            public:

              ...                              // 同上

             

            private:

              std::string name;

              Date lastTransaction;

            };

            現(xiàn)在,前面的拷貝函數(shù)將進行部分復制:它們會復制出顧客的姓名(name),但是不會復制最后一次交易(lastTransaction)。迄今為止大多數(shù)編譯器對此視而不見,即使將警告調(diào)至最大模式也不會報出任何信息(另請參見條目53)。如果你自己編寫拷貝函數(shù),這便是這些編譯器對你的“復仇”。由于你拒絕了編譯器提供的拷貝函數(shù),那么編譯器就拒絕在你的代碼不完整時通知你。結(jié)果很明顯:如果你為一個類添加了一個數(shù)據(jù)成員,你必須確保更新相應的拷貝函數(shù)。(同時你也需要更新所有的構(gòu)造函數(shù)(參見條目4和條目45),以及類中所有的非標準格式的operator=(參見條目10中的示例)。如果你忘記了,編譯器也不會及時提醒你。)

            通過繼承,這一問題可以帶來更加嚴重卻隱蔽的危害,請考慮下邊的示例:

            class PriorityCustomer: public Customer {  // 一個派生類

            public:

               ...

               PriorityCustomer(const PriorityCustomer& rhs);

               PriorityCustomer& operator=(const PriorityCustomer& rhs);

               ...

             

            private:

               int priority;

            };

             

            PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)

            : priority(rhs.priority)

            {

              logCall("PriorityCustomer copy constructor");

            }

             

            PriorityCustomer&

            PriorityCustomer::operator=(const PriorityCustomer& rhs)

            {

              logCall("PriorityCustomer copy assignment operator");

              priority = rhs.priority;

              return *this;

            }

            PriorityCustomer的拷貝函數(shù)看上去能復制PriorityCustomer中的所有數(shù)據(jù),但是請仔細看一下,的確,這些拷貝函數(shù)確實能夠復制PriorityCustomer中聲明的數(shù)據(jù)成員,但是每一個PriorityCustomer對象都還包含繼承自Customer的所有數(shù)據(jù)成員,這些數(shù)據(jù)成員始終沒有得到復制!PriorityCustomer的拷貝構(gòu)造函數(shù)并沒有指明任何參數(shù)去傳遞至基類的構(gòu)造函數(shù)(也就是說,它在成員初始化表中從未提及Customer),于是PriorityCustomerCustomer那一部分將由Customer的無參構(gòu)造函數(shù)——默認構(gòu)造函數(shù)(假設(shè)存在一個,如果沒有編譯器將報錯)進行初始化。這一構(gòu)造函數(shù)將為namelastTransation進行默認的初始化。

            對于PriorityCustomer的拷貝賦值運算符而言,情況有小小的不同。在任何情況下它都不會嘗試去修改其基類的數(shù)據(jù)成員,所以這些數(shù)據(jù)成員不會得到更新。

            一旦你親自為一個繼承類編寫了拷貝函數(shù),你必須同時留心其基類的部分。當然這些部分通常情況下是私有的,所以你無法直接訪問它們。取而代之的是,派生類的拷貝函數(shù)必須調(diào)用這些私有數(shù)據(jù)在基類中相關(guān)的函數(shù):

            PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)

            : Customer(rhs),                   // 調(diào)用基類的拷貝構(gòu)造函數(shù)

              priority(rhs.priority)

            {

              logCall("PriorityCustomer copy constructor");

            }

             

            PriorityCustomer&

            PriorityCustomer::operator=(const PriorityCustomer& rhs)

            {

              logCall("PriorityCustomer copy assignment operator");

              Customer::operator=(rhs);        // 為基類部分賦值

              priority = rhs.priority;

              return *this;

            }

            本條目標題中的“不要遺漏任何部分”在這里就顯得很清晰了。當你編寫拷貝函數(shù)時,要確認:(1)復制所有的局部數(shù)據(jù)成員,(2) 對所有的基類調(diào)用適當?shù)目截惡瘮?shù)。

            從實踐角度講,這兩個拷貝函數(shù)通常會很相似,這似乎會慫恿你去嘗試讓一個函數(shù)去調(diào)用另一個來避免代碼重復。你避免代碼重復的渴望之心是值得稱贊的,但是讓一個拷貝函數(shù)調(diào)用另一個并不是一個好的實現(xiàn)方式。

            讓拷貝賦值運算符調(diào)用拷貝構(gòu)造函數(shù)是沒有任何意義的,這是因為你是在嘗試構(gòu)造一個已經(jīng)存在的對象。這實在是毫無意義,甚至沒有語法支持這樣做。即使存在語法看上去可以讓你這樣做,但事實上你達不到預期的目的;同時存在語法確實可以迂回實現(xiàn),但是卻會在一些情況下破壞你的對象。所以我不會向你介紹這些語法。你只需清楚讓拷貝賦值運算符去調(diào)用拷貝構(gòu)造函數(shù)不是一個好主意就可以了。

            讓我們逆向考慮此問題——讓拷貝構(gòu)造函數(shù)去調(diào)用拷貝賦值運算符——這樣做同樣是毫無意義的。一個構(gòu)造函數(shù)初始化新的對象,但是一個賦值運算符僅僅可以應用于已經(jīng)初始化的對象。對于一個正在構(gòu)造中的對象而言,對其進行賦值操作就意味著對未初始化的對象進行操作(但是這些操作僅對已初始化的對象起作用)。這是很荒唐的,請不要嘗試。

            如果你發(fā)現(xiàn)你的拷貝構(gòu)造函數(shù)和拷貝賦值運算符很相似時,如果你希望排除重復代碼,可以通過創(chuàng)建第三個成員函數(shù)供兩者調(diào)用來取代上面的方法。通常情況下,這樣的函數(shù)應該是私有的,一般將其命名為init。這樣的策略是安全的,排除拷貝構(gòu)造函數(shù)和拷貝賦值運算符中的代碼重復,這是一個經(jīng)過證實安全有效的方法。

            時刻牢記

            要確保拷貝函數(shù)拷貝對象的所有的數(shù)據(jù)成員,及其基類的所有部分,不要有遺漏。

            不要嘗試去實現(xiàn)一個拷貝函數(shù)來供其它的拷貝函數(shù)調(diào)用。你應該把公共部分放入一個“第三方函數(shù)”中共所有拷貝函數(shù)調(diào)用。

            posted on 2007-05-03 21:15 ★ROY★ 閱讀(875) 評論(1)  編輯 收藏 引用 所屬分類: Effective C++

            評論

            # re: 【翻譯】Effective C++ (第12條:要復制整個對象,不要遺漏任一部分)  回復  更多評論   

            對于 PriorityCustomer 的拷貝賦值操作符而言,情況有小小的不同。在任何情況下它都不會嘗試去修改其基類的數(shù)據(jù)成員,所以這些數(shù)據(jù)成員永遠得不到更新。
            2008-07-15 21:06 | Wu
            久久亚洲欧美国产精品| 精品综合久久久久久88小说 | 亚洲国产精品婷婷久久| 精品国产91久久久久久久a| 国产精品久久久久久久午夜片| 国产亚洲精久久久久久无码AV| 久久亚洲国产最新网站| 久久精品国产精品亚洲毛片 | 久久国产精品99精品国产| 中文精品久久久久国产网址| 久久精品国产亚洲AV久| 欧美综合天天夜夜久久| 亚洲精品tv久久久久久久久 | 久久99精品久久久久久9蜜桃| 伊人久久精品无码二区麻豆| 久久福利片| 777米奇久久最新地址| 久久久精品人妻一区二区三区蜜桃 | 伊人热热久久原色播放www| 久久成人精品视频| 久久精品黄AA片一区二区三区| 日本国产精品久久| 久久国产精品一区| 国产成人久久精品二区三区| 国产欧美久久一区二区| www.久久热| 亚洲国产成人久久精品影视| 国产精品久久久久久福利漫画| 欧美一区二区三区久久综| 性高湖久久久久久久久| 国产成人无码精品久久久性色| 欧美一级久久久久久久大| 久久久黄片| 中文成人久久久久影院免费观看| 99久久国产亚洲高清观看2024 | 久久AV高潮AV无码AV| 久久无码AV中文出轨人妻| 97精品伊人久久久大香线蕉| 97香蕉久久夜色精品国产 | 99久久www免费人成精品| 四虎国产精品免费久久5151|