• <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
            日韩美女18网站久久精品| 精品欧美一区二区三区久久久 | 亚洲精品午夜国产VA久久成人| 亚洲天堂久久久| 久久久噜噜噜www成人网| 成人综合伊人五月婷久久| 狠狠色综合久久久久尤物| 久久天天躁狠狠躁夜夜avapp| 精品人妻久久久久久888| 久久精品夜色噜噜亚洲A∨| 亚洲国产精品无码久久一区二区| 国产L精品国产亚洲区久久| 久久人人爽人人爽人人AV| 久久99久久无码毛片一区二区| 亚洲va久久久噜噜噜久久天堂| 久久99精品国产麻豆蜜芽| 久久亚洲春色中文字幕久久久| 性做久久久久久久久老女人| 久久美女人爽女人爽| 奇米影视7777久久精品| 免费精品久久天干天干| 久久精品国产亚洲7777| 亚洲国产精品人久久| 日韩精品久久久久久免费| 狠狠色丁香久久婷婷综合图片| 国产精品成人99久久久久 | 亚洲国产欧美国产综合久久| 久久精品国产只有精品66| 激情伊人五月天久久综合| 人妻无码αv中文字幕久久| 久久久亚洲裙底偷窥综合| 狠狠色丁香久久婷婷综合蜜芽五月| 大香网伊人久久综合网2020| 91精品国产高清久久久久久国产嫩草 | 欧美大香线蕉线伊人久久| 狠狠精品久久久无码中文字幕| 欧洲性大片xxxxx久久久| 亚洲午夜精品久久久久久app| 亚洲а∨天堂久久精品| 精品国产日韩久久亚洲| 久久久噜噜噜久久中文字幕色伊伊 |