• <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é)無(wú)止境

            Step By Step

            統(tǒng)計(jì)

            留言簿

            C++ Programmer's Cookbook

            外刊IT評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            【轉(zhuǎn)】復(fù)制構(gòu)造函數(shù)(拷貝構(gòu)造函數(shù))

            轉(zhuǎn)自此處,貌似博主也是轉(zhuǎn)別人的

            也許很多C++的初學(xué)者都知道什么是構(gòu)造函數(shù),但是對(duì)復(fù)制構(gòu)造函數(shù)(copy constructor)卻還很陌生。對(duì)于我來(lái)說(shuō),在寫(xiě)代碼的時(shí)候能用得上復(fù)制構(gòu)造函數(shù)的機(jī)會(huì)并不多,不過(guò)這并不說(shuō)明復(fù)制構(gòu)造函數(shù)沒(méi)什么用,其實(shí)復(fù)制構(gòu)造函數(shù)能解決一些我們常常會(huì)忽略的問(wèn)題。
                   為了說(shuō)明復(fù)制構(gòu)造函數(shù)作用,我先說(shuō)說(shuō)我們?cè)诰幊虝r(shí)會(huì)遇到的一些問(wèn)題。 對(duì)于C++中的函數(shù),我們應(yīng)該很熟悉了,因?yàn)槠匠=?jīng)常使用;對(duì)于類(lèi)的對(duì)象,我們也很熟悉,因?yàn)槲覀円步?jīng)常寫(xiě)各種各樣的類(lèi),使用各種各樣的對(duì)象;對(duì)于指針的 操作,我們也不陌生吧?嗯,如果你還不了解上面三個(gè)概念的話,我想這篇文章不太適合你,不過(guò)看看也無(wú)礙^_^。我們經(jīng)常使用函數(shù),傳遞過(guò)各種各樣的參數(shù)給 函數(shù),不過(guò)把對(duì)象(注意是對(duì)象,而不是對(duì)象的指針或?qū)ο蟮囊茫┊?dāng)作參數(shù)傳給函數(shù)的情況我們應(yīng)該比較少遇見(jiàn)吧,而且這個(gè)對(duì)象的構(gòu)造函數(shù)還涉及到一些內(nèi)存分配的操作。嗯,這樣會(huì)有什么問(wèn)題呢?
                   把參數(shù)傳遞給函數(shù)有三種方法,一種是值傳遞,一種是傳地址,還有一種 是傳引用。前者與后兩者不同的地方在于:當(dāng)使用值傳遞的時(shí)候,會(huì)在函數(shù)里面生成傳遞參數(shù)的一個(gè)副本,這個(gè)副本的內(nèi)容是按位從原始參數(shù)那里復(fù)制過(guò)來(lái)的,兩者 的內(nèi)容是相同的。當(dāng)原始參數(shù)是一個(gè)類(lèi)的對(duì)象時(shí),它也會(huì)產(chǎn)生一個(gè)對(duì)象的副本,不過(guò)在這里要注意。一般對(duì)象產(chǎn)生時(shí)都會(huì)觸發(fā)構(gòu)造函數(shù)的執(zhí)行,但是在產(chǎn)生對(duì)象的副 本時(shí)卻不會(huì)這樣,這時(shí)執(zhí)行的是對(duì)象的復(fù)制構(gòu)造函數(shù)。為什么會(huì)這樣?嗯,一般的構(gòu)造函數(shù)都是會(huì)完成一些成員屬性初始化的工作,在對(duì)象傳遞給某一函數(shù)之前,對(duì) 象的一些屬性可能已經(jīng)被改變了,如果在產(chǎn)生對(duì)象副本的時(shí)候再執(zhí)行對(duì)象的構(gòu)造函數(shù),那么這個(gè)對(duì)象的屬性又再恢復(fù)到原始狀態(tài),這并不是我們想要的。所以在產(chǎn)生對(duì)象副本的時(shí)候,構(gòu)造函數(shù)不會(huì)被執(zhí)行,被執(zhí)行的是一個(gè)默認(rèn)的構(gòu)造函數(shù)。當(dāng)函數(shù)執(zhí)行完畢要返回的時(shí)候,對(duì)象副本會(huì)執(zhí)行析構(gòu)函數(shù), 如果你的析構(gòu)函數(shù)是空的話, 就不會(huì)發(fā)生什么問(wèn)題,但一般的析構(gòu)函數(shù)都是要完成一些清理工作,如釋放指針?biāo)赶虻膬?nèi)存空間。這時(shí)候問(wèn)題就可能要出現(xiàn)了。假如你在構(gòu)造函數(shù)里面為一個(gè)指針 變量分配了內(nèi)存,在析構(gòu)函數(shù)里面釋放分配給這個(gè)指針?biāo)赶虻膬?nèi)存空間,那么在把對(duì)象傳遞給函數(shù)至函數(shù)結(jié)束返回這一過(guò)程會(huì)發(fā)生什么事情呢?首先有一個(gè)對(duì)象的 副本產(chǎn)生了,這個(gè)副本也有一個(gè)指針,它和原始對(duì)象的指針是指向同塊內(nèi)存空間的。函數(shù)返回時(shí),對(duì)象的析構(gòu)函數(shù)被執(zhí)行了,即釋放了對(duì)象副本里面指針?biāo)赶虻膬?nèi) 存空間,但是這個(gè)內(nèi)存空間對(duì)原始對(duì)象還是有用的啊,就程序本身而言,這是一個(gè)嚴(yán)重的錯(cuò)誤。然而錯(cuò)誤還沒(méi)結(jié)束,當(dāng)原始對(duì)象也被銷(xiāo)毀的時(shí)候,析構(gòu)函數(shù)再次執(zhí) 行,對(duì)同一塊系統(tǒng)動(dòng)態(tài)分配的內(nèi)存空間釋放兩次是一個(gè)未知的操作,將會(huì)產(chǎn)生嚴(yán)重的錯(cuò)誤。
                   上面說(shuō)的就是我們會(huì)遇到的問(wèn)題。解決問(wèn)題的方法是什么呢?首先我們想 到的是不要以傳值的方式來(lái)傳遞參數(shù),我們可以用傳地址或傳引用。沒(méi)錯(cuò),這樣的確可以避免上面的情況,而且在允許的情況下,傳地址或傳引用是最好的方法,但 這并不適合所有的情況,有時(shí)我們不希望在函數(shù)里面的一些操作會(huì)影響到函數(shù)外部的變量。那要怎么辦呢?可以利用復(fù)制構(gòu)造函數(shù)來(lái)解決這一問(wèn)題。復(fù)制構(gòu)造函數(shù)就是在產(chǎn)生對(duì)象副本的時(shí)候執(zhí)行的,我們可以定義自己的復(fù)制構(gòu)造函數(shù)。在復(fù)制構(gòu)造函數(shù)里面我們申請(qǐng)一個(gè)新的內(nèi)存空間來(lái)保存構(gòu)造函數(shù)里面的那個(gè)指針?biāo)赶虻膬?nèi)容。這樣在執(zhí)行對(duì)象副本的析構(gòu)函數(shù)時(shí),釋放的就是復(fù)制構(gòu)造函數(shù)里面所申請(qǐng)的那個(gè)內(nèi)存空間。
                   除了將對(duì)象傳遞給函數(shù)時(shí)會(huì)存在以上問(wèn)題,還有一種情況也會(huì)存在以上問(wèn)題,就是當(dāng)函數(shù)返回對(duì)象時(shí),會(huì)產(chǎn)生一個(gè)臨時(shí)對(duì)象,這個(gè)臨時(shí)對(duì)象和對(duì)象的副本性質(zhì)差不多。
            拷貝構(gòu)造函數(shù),經(jīng)常被稱作X(X&),是一種特殊的構(gòu)造函數(shù),他由編譯器調(diào)用來(lái)完成一些基于同一類(lèi)的其他對(duì)象的構(gòu)件及初始化。它的唯一的一個(gè)參數(shù)(對(duì)象的引用)是不可變的(因?yàn)槭莄onst型的)。這個(gè)函數(shù)經(jīng)常用在函數(shù)調(diào)用期間于用戶定義類(lèi)型的值傳遞及返回。拷貝構(gòu)造函數(shù)要調(diào)用基類(lèi)的拷貝構(gòu)造函數(shù)和成員函數(shù)。如果可以的話,它將用常量方式調(diào)用,另外,也可以用非常量方式調(diào)用。
            在C++中,下面三種對(duì)象需要拷貝的情況。因此,拷貝構(gòu)造函數(shù)將會(huì)被調(diào)用。
            1). 一個(gè)對(duì)象以值傳遞的方式傳入函數(shù)體
            2). 一個(gè)對(duì)象以值傳遞的方式從函數(shù)返回
            3). 一個(gè)對(duì)象需要通過(guò)另外一個(gè)對(duì)象進(jìn)行初始化
            以 上的情況需要拷貝構(gòu)造函數(shù)的調(diào)用。如果在前兩種情況不使用拷貝構(gòu)造函數(shù)的時(shí)候,就會(huì)導(dǎo)致一個(gè)指針指向已經(jīng)被刪除的內(nèi)存空間。對(duì)于第三種情況來(lái)說(shuō),初始化和賦值的不同含義是構(gòu)造函數(shù)調(diào)用的原因。事實(shí)上,拷貝構(gòu)造函數(shù)是由普通構(gòu)造函數(shù)和賦值操作符共同實(shí)現(xiàn)的。描述拷貝構(gòu)造函數(shù)和賦值運(yùn)算符的異同的參考資料有很多。
            拷貝構(gòu)造函數(shù)不可以改變它所引用的對(duì)象,其原因如下:當(dāng)一個(gè)對(duì)象以傳遞值的方式傳一個(gè)函數(shù)的時(shí)候,拷貝構(gòu)造函數(shù)自動(dòng)的被調(diào)用來(lái)生成函數(shù)中的對(duì)象。如果一個(gè)對(duì)象是被傳入自己的拷貝構(gòu)造函數(shù),它的拷貝構(gòu)造函數(shù)將會(huì)被調(diào)用來(lái)拷貝這個(gè)對(duì)象這樣復(fù)制才可以傳入它自己的拷貝構(gòu)造函數(shù),這會(huì)導(dǎo)致無(wú)限循 環(huán)。
            除了當(dāng)對(duì)象傳入函數(shù)的時(shí)候被隱式調(diào)用以外,拷貝構(gòu)造函數(shù)在對(duì)象被函數(shù)返回的時(shí)候也同樣的被調(diào)用。換句話說(shuō),你從函數(shù)返回得到的只是對(duì)象的一份拷貝。但是同樣的,拷貝構(gòu)造函數(shù)被正確的調(diào)用了,你不必?fù)?dān)心。
            如果在類(lèi)中沒(méi)有顯式的聲明一個(gè)拷貝構(gòu)造函數(shù),那么,編譯器會(huì)私下里為你制定一個(gè)函數(shù)來(lái)進(jìn)行對(duì)象之間的位拷貝(bitwise copy)。這個(gè)隱含的拷貝構(gòu)造函數(shù)簡(jiǎn)單的關(guān)聯(lián)了所有的類(lèi)成員。許多作者都會(huì)提及這個(gè)默認(rèn)的拷貝構(gòu)造函數(shù)。注意到這個(gè)隱式的拷貝構(gòu)造函數(shù)和顯式聲明的拷貝構(gòu)造函數(shù)的不同在于對(duì)于成員的關(guān)聯(lián)方式。顯式聲明的拷貝構(gòu)造函數(shù)關(guān)聯(lián)的只是被實(shí)例化的類(lèi)成員的缺省構(gòu)造函數(shù)除非另外一個(gè)構(gòu)造函數(shù)在類(lèi)初始化或者在構(gòu)造列表的時(shí)候被調(diào)用。
            拷貝構(gòu)造函數(shù)使程序更加有效率,因?yàn)樗挥迷贅?gòu)造一個(gè)對(duì)象的時(shí)候改變構(gòu)造函數(shù)的參數(shù)列表。設(shè)計(jì)拷貝構(gòu)造函數(shù)是一個(gè)良好的風(fēng)格,即使是編譯系統(tǒng)提供的幫助你申請(qǐng)內(nèi)存默認(rèn)拷貝構(gòu)造函數(shù)。事實(shí)上,默認(rèn)拷貝構(gòu)造函數(shù)可以應(yīng)付許多情況。
            附另外一篇關(guān)于復(fù)制構(gòu)造函數(shù)的文章:

            對(duì)一個(gè)簡(jiǎn)單變量的初始化方法是用一個(gè)常量或變量初始化另一個(gè)變量,例如:

            int m = 80;
            int n = m;

              我們已經(jīng)會(huì)用構(gòu)造函數(shù)初始化對(duì)象,那么我們能不能象簡(jiǎn)單變量的初始化一樣,直接用一個(gè)對(duì)象來(lái)初始化另一個(gè)對(duì)象呢?答案是肯定的。我們以前面定義的Point類(lèi)為例:
            Point pt1(1525);
            Point pt2 
            = pt1;

            后一個(gè)語(yǔ)句也可以寫(xiě)成:
            Point pt2( pt1);

            它 是用pt1初始化pt2,此時(shí),pt2各個(gè)成員的值與pt1各個(gè)成員的值相同,也就是說(shuō),pt1各個(gè)成員的值被復(fù)制到pt2相應(yīng)的成員當(dāng)中。在這個(gè)初始化 過(guò)程當(dāng)中,實(shí)際上調(diào)用了一個(gè)復(fù)制構(gòu)造函數(shù)。當(dāng)我們沒(méi)有顯式定義一個(gè)復(fù)制構(gòu)造函數(shù)時(shí),編譯器會(huì)隱式定義一個(gè)缺省的復(fù)制構(gòu)造函數(shù),它是一個(gè)內(nèi)聯(lián)的、公有的成 員,它具有下面的原型形式:
            Point:: Point (const Point &);

            可見(jiàn),復(fù)制構(gòu)造函數(shù)與構(gòu)造函數(shù)的不同之處在于形參,前者的形參是Point對(duì)象的引用,其功能是將一個(gè)對(duì)象的每一個(gè)成員復(fù)制到另一個(gè)對(duì)象對(duì)應(yīng)的成員當(dāng)中。
              雖然沒(méi)有必要,我們也可以為Point類(lèi)顯式定義一個(gè)復(fù)制構(gòu)造函數(shù):
            Point:: Point (const Point &pt)
            {
              xVal
            =pt. xVal;
              yVal
            =pt. yVal;

            如果一個(gè)類(lèi)中有指針成員,使用缺省的復(fù)制構(gòu)造函數(shù)初始化對(duì)象就會(huì)出現(xiàn)問(wèn)題。為了說(shuō)明存在的問(wèn)題,我們假定對(duì)象A與對(duì)象B是相同的類(lèi),有一個(gè)指針成員,指 向?qū)ο驝。當(dāng)用對(duì)象B初始化對(duì)象A時(shí),缺省的復(fù)制構(gòu)造函數(shù)將B中每一個(gè)成員的值復(fù)制到A的對(duì)應(yīng)的成員當(dāng)中,但并沒(méi)有復(fù)制對(duì)象C。也就是說(shuō),對(duì)象A和對(duì)象B 中的指針成員均指向?qū)ο驝,實(shí)際上,我們希望對(duì)象C也被復(fù)制,得到C的對(duì)象副本D。否則,當(dāng)對(duì)象A和B銷(xiāo)毀時(shí),會(huì)對(duì)對(duì)象C的內(nèi)存區(qū)重復(fù)釋放,而導(dǎo)致錯(cuò)誤。 為了使對(duì)象C也被復(fù)制,就必須顯式定義復(fù)制構(gòu)造函數(shù)。下面我們以string類(lèi)為例說(shuō)明,如何定義這個(gè)復(fù)制構(gòu)造函數(shù)。

            例題
            例10-11
             
             1 class String
             2 {
             3  public:
             4   String(); //構(gòu)造函數(shù)
             5   String(const String &s); //復(fù)制構(gòu)造函數(shù)
             6   ~String(); //析構(gòu)函數(shù)
             7 
             8   // 接口函數(shù)
             9   void set(char const *data);
            10   char const *get(void);
            11 
            12  private:
            13   char *str; //數(shù)據(jù)成員ptr指向分配的字符串
            14 };
            15 String ::String(const String &s)
            16 {
            17  str = new char[strlen(s.str) + 1];
            18  strcpy(str, s.str);
            19 }

            我們也常用無(wú)名對(duì)象初始化另一個(gè)對(duì)象,例如:

            Point pt = Point(1020);

              類(lèi)名直接調(diào)用構(gòu)造函數(shù)就生成了一個(gè)無(wú)名對(duì)象,上式用左邊的無(wú)名對(duì)象初始化右邊的pt對(duì)象。
              構(gòu)造函數(shù)被調(diào)用通常發(fā)生在以下三種情況,第一種情況就是我們上面看到的:用一個(gè)對(duì)象初始化另一個(gè)對(duì)象時(shí);第二種情況是當(dāng)對(duì)象作函數(shù)參數(shù),實(shí)參傳給形參時(shí);第三種情況是程序運(yùn)行過(guò)程中創(chuàng)建其它臨時(shí)對(duì)象時(shí)。下面我們?cè)倥e一個(gè)例子,就第二種情況和第三種情況進(jìn)行說(shuō)明:
            Point foo(Point pt)
              {
               …
               
            return pt;
              }
              
            void main()
              {
               Point pt1 
            = Point(1020);
               Point pt2;
               …
               pt2
            =foo(pt);
               …
              }

              在main函數(shù)中調(diào)用foo函數(shù)時(shí),實(shí)參pt傳給形參pt,將實(shí)參pt復(fù)制給形參pt,要調(diào)用復(fù)制構(gòu)造函數(shù),當(dāng)函數(shù)foo返回時(shí),要?jiǎng)?chuàng)建一個(gè)pt的臨時(shí)對(duì)象,此時(shí)也要調(diào)用復(fù)制構(gòu)造函數(shù)。

             

            缺省的復(fù)制構(gòu)造函數(shù)
              在類(lèi)的定義中,如果沒(méi)有顯式定義復(fù)制構(gòu)造函數(shù),C++編譯器會(huì)自動(dòng)地定義一個(gè)缺省的復(fù)制構(gòu)造函數(shù)。下面是使用復(fù)制構(gòu)造函數(shù)的一個(gè)例子:

            例題
            例10-12
             
             1 #include <iostream.h>
             2 #include <string.h>
             3 class withCC
             4 {
             5  public:
             6  withCC(){}
             7  withCC(const withCC&)
             8  {
             9   cout<<"withCC(withCC&)"<<endl;
            10  }
            11 };
            12 
            13 class woCC
            14 {
            15  enum{bsz = 100};
            16  char buf[bsz];
            17 public:
            18  woCC(const char* msg = 0)
            19  {
            20   memset(buf, 0, bsz);
            21   if(msg) strncpy(buf, msg, bsz);
            22  }
            23  void print(const char* msg = 0)const
            24  {
            25   if(msg) cout<<msg<<":";
            26   cout<<buf<<endl;
            27  }
            28 };
            29 
            30 class composite
            31 {
            32  withCC WITHCC;
            33  woCC WOCC;
            34 public:
            35  composite() : WOCC("composite()"){}
            36  void print(const char* msg = 0)
            37  {
            38   WOCC.print(msg);
            39  }
            40 };
            41 
            42 void main()
            43 {
            44  composite c;
            45  c.print("contents of c");
            46  cout<<"calling composite copy-constructor"<<endl;
            47  composite c2 = c;
            48  c2.print("contents of c2");
            49 

            類(lèi)withCC有一個(gè)復(fù)制構(gòu)造函數(shù),類(lèi)woCC和類(lèi)composite都沒(méi)有顯式定義復(fù)制構(gòu)造函數(shù)。如果在類(lèi)中沒(méi)有顯式定義復(fù)制構(gòu)造函數(shù),則編譯器將自動(dòng)地創(chuàng)建一個(gè)缺省的構(gòu)造函數(shù)。不過(guò)在這種情況下,這個(gè)構(gòu)造函數(shù)什么也不作。
              類(lèi)composite既含有withCC類(lèi)的成員對(duì)象又含有woCC類(lèi)的成員對(duì)象,它使用無(wú)參的構(gòu)造函數(shù)創(chuàng)建withCC類(lèi)的對(duì)象WITHCC(注意內(nèi)嵌的對(duì)象WOCC的初始化方法)。
              在main()函數(shù)中,語(yǔ)句:
              composite c2 = c;
            通過(guò)對(duì)象C初始化對(duì)象c2,缺省的復(fù)制構(gòu)造函數(shù)被調(diào)用。
              最好的方法是創(chuàng)建自己的復(fù)制構(gòu)造函數(shù)而不要指望編譯器創(chuàng)建,這樣就能保證程序在我們自己的控制之下。

            posted on 2010-04-14 11:03 wstonep 閱讀(247) 評(píng)論(0)  編輯 收藏 引用

            欧美亚洲国产精品久久蜜芽| 久久久久人妻一区精品色 | 亚洲国产成人久久一区久久 | 国产成人久久久精品二区三区| 亚洲AV无码久久寂寞少妇| 伊人久久大香线蕉精品不卡| 日韩精品无码久久一区二区三| 久久精品国产亚洲AV不卡| 久久九色综合九色99伊人| 久久毛片免费看一区二区三区| 久久精品国产一区二区三区| 精品久久久久久国产牛牛app| 久久成人18免费网站| 日韩久久久久中文字幕人妻 | 久久成人国产精品二三区| 国内精品久久九九国产精品| 99久久精品国产高清一区二区 | 久久伊人精品青青草原日本| 久久久网中文字幕| 精品久久久中文字幕人妻| 亚洲人成网亚洲欧洲无码久久| 久久综合九色综合网站| 久久九九青青国产精品| 亚洲欧美一级久久精品| 久久九九久精品国产免费直播| 中文字幕日本人妻久久久免费| 久久久久亚洲av无码专区导航| 久久免费高清视频| 99久久这里只精品国产免费| 日韩精品久久久久久免费| 91久久精品视频| 久久九九免费高清视频| 天天躁日日躁狠狠久久| 国产精品久久久久影视不卡| 久久久久亚洲精品中文字幕| 久久午夜无码鲁丝片| 欧美激情精品久久久久久| 久久天天躁狠狠躁夜夜avapp| 精品乱码久久久久久夜夜嗨| 日本强好片久久久久久AAA| 老司机午夜网站国内精品久久久久久久久 |