小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

拷貝構(gòu)造函數(shù)

 306 2009-05-02
拷貝構(gòu)造函數(shù),經(jīng)常被稱作X(X&),是一種特殊的構(gòu)造函數(shù),他由編譯器調(diào)用來(lái)完成一些基于同一類的其他對(duì)象的構(gòu)件及初始化。它的唯一的一個(gè)參數(shù)(對(duì)象的引用)是不可變的(因?yàn)槭莄onst型的)。這個(gè)函數(shù)經(jīng)常用在函數(shù)調(diào)用期間于用戶定義類型的值傳遞及返回。拷貝構(gòu)造函數(shù)要調(diào)用基類的拷貝構(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)心。
如果在類中沒有顯式的聲明一個(gè)拷貝構(gòu)造函數(shù),那么,編譯器會(huì)私下里為你制定一個(gè)函數(shù)來(lái)進(jìn)行對(duì)象之間的位拷貝(bitwise copy)。這個(gè)隱含的拷貝構(gòu)造函數(shù)簡(jiǎn)單的關(guān)聯(lián)了所有的類成員。許多作者都會(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í)例化的類成員的缺省構(gòu)造函數(shù)除非另外一個(gè)構(gòu)造函數(shù)在類初始化或者在構(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)付許多情況。
以下討論中將用到的例子:
class CExample
{
public:
CExample(){pBuffer=NULL; nSize=0;}
~CExample(){delete pBuffer;}
void Init(int n){ pBuffer=new char[n]; nSize=n;}
private:
char *pBuffer; //類的對(duì)象中包含指針,指向動(dòng)態(tài)分配的內(nèi)存資源
int nSize;
};

這個(gè)類的主要特點(diǎn)是包含指向其他資源的指針。
pBuffer指向堆中分配的一段內(nèi)存空間。

一、拷貝構(gòu)造函數(shù)
int main(int argc, char* argv[])
{
CExample theObjone;
theObjone.Init40);

//現(xiàn)在需要另一個(gè)對(duì)象,需要將他初始化稱對(duì)象一的狀態(tài)
CExample theObjtwo=theObjone;
...
}

語(yǔ)句"CExample theObjtwo=theObjone;"用theObjone初始化theObjtwo。
其完成方式是內(nèi)存拷貝,復(fù)制所有成員的值。
完成后,theObjtwo.pBuffer==theObjone.pBuffer。
即它們將指向同樣的地方,指針雖然復(fù)制了,但所指向的空間并沒有復(fù)制,而是由兩個(gè)對(duì)象共用了。這樣不符合要求,對(duì)象之間不獨(dú)立了,并為空間的刪除帶來(lái)隱患。
所以需要采用必要的手段來(lái)避免此類情況。
回顧以下此語(yǔ)句的具體過(guò)程:首先建立對(duì)象theObjtwo,并調(diào)用其構(gòu)造函數(shù),然后成員被拷貝。
可以在構(gòu)造函數(shù)中添加操作來(lái)解決指針成員的問(wèn)題。
所以C++語(yǔ)法中除了提供缺省形式的構(gòu)造函數(shù)外,還規(guī)范了另一種特殊的構(gòu)造函數(shù):拷貝構(gòu)造函數(shù),上面的語(yǔ)句中,如果類中定義了拷貝構(gòu)造函數(shù),這對(duì)象建立時(shí),調(diào)用的將是拷貝構(gòu)造函數(shù),在拷貝構(gòu)造函數(shù)中,可以根據(jù)傳入的變量,復(fù)制指針?biāo)赶虻馁Y源。

拷貝構(gòu)造函數(shù)的格式為:構(gòu)造函數(shù)名(對(duì)象的引用)
提供了拷貝構(gòu)造函數(shù)后的CExample類定義為:
class CExample
{
public:
CExample(){pBuffer=NULL; nSize=0;}
~CExample(){delete pBuffer;}
CExample(const CExample&); //拷貝構(gòu)造函數(shù)
void Init(int n){ pBuffer=new char[n]; nSize=n;}
private:
char *pBuffer; //類的對(duì)象中包含指針,指向動(dòng)態(tài)分配的內(nèi)存資源
int nSize;
};
CExample::CExample(const CExample& RightSides) //拷貝構(gòu)造函數(shù)的定義
{
nSize=RightSides.nSize; //復(fù)制常規(guī)成員
pBuffer=new char[nSize]; //復(fù)制指針指向的內(nèi)容
memcpy(pBuffer,RightSides.pBuffer,nSize*sizeof(char));
}

這樣,定義新對(duì)象,并用已有對(duì)象初始化新對(duì)象時(shí),CExample(const CExample& RightSides)將被調(diào)用,而已有對(duì)象用別名RightSides傳給構(gòu)造函數(shù),以用來(lái)作復(fù)制。

原則上,應(yīng)該為所有包含動(dòng)態(tài)分配成員的類都提供拷貝構(gòu)造函數(shù)。

拷貝構(gòu)造函數(shù)的另一種調(diào)用。

當(dāng)對(duì)象直接作為參數(shù)傳給函數(shù)時(shí),函數(shù)將建立對(duì)象的臨時(shí)拷貝,這個(gè)拷貝過(guò)程也將調(diào)同拷貝構(gòu)造函數(shù)。
例如
BOOL testfunc(CExample obj);
testfunc(theObjone); //對(duì)象直接作為參數(shù)。
BOOL testfunc(CExample obj)
{
//針對(duì)obj的操作實(shí)際上是針對(duì)復(fù)制后的臨時(shí)拷貝進(jìn)行的
}

還有一種情況,也是與臨時(shí)對(duì)象有關(guān)的
當(dāng)函數(shù)中的局部對(duì)象被被返回給函數(shù)調(diào)者時(shí),也將建立此局部對(duì)象的一個(gè)臨時(shí)拷貝,拷貝構(gòu)造函數(shù)也將被調(diào)用

CTest func()
{
CTest theTest;
return theTest
}

二、賦值符的重載
下面的代碼與上例相似
int main(int argc, char* argv[])
{
CExample theObjone;
theObjone.Init(40);

CExample theObjthree;
theObjthree.Init(60);
//現(xiàn)在需要一個(gè)對(duì)象賦值操作,被賦值對(duì)象的原內(nèi)容被清除,并用右邊對(duì)象的內(nèi)容填充。
theObjthree=theObjone;
return 0;
}

也用到了"="號(hào),但與"一、"中的例子并不同,"一、"的例子中,"="在對(duì)象聲明語(yǔ)句中,表示初始化。更多時(shí)候,這種初始化也可用括號(hào)表示。
例如 CExample theObjone(theObjtwo);
而本例子中,"="表示賦值操作。將對(duì)象theObjone的內(nèi)容復(fù)制到對(duì)象theObjthree;,這其中涉及到對(duì)象theObjthree原有內(nèi)容的丟棄,新內(nèi)容的復(fù)制。
但"="的缺省操作只是將成員變量的值相應(yīng)復(fù)制。舊的值被自然丟棄。
由于對(duì)象內(nèi)包含指針,將造成不良后果:指針的值被丟棄了,但指針指向的內(nèi)容并未釋放。指針的值被復(fù)制了,但指針?biāo)竷?nèi)容并未復(fù)制。

因此,包含動(dòng)態(tài)分配成員的類除提供拷貝構(gòu)造函數(shù)外,還應(yīng)該考慮重載"="賦值操作符號(hào)。
類定義變?yōu)?
class CExample
{
...
CExample(const CExample&); //拷貝構(gòu)造函數(shù)
CExample& operator = (const CExample&); //賦值符重載
...
};

//賦值操作符重載
CExample & CExample::operator = (const CExample& RightSides)
{
nSize=RightSides.nSize; //復(fù)制常規(guī)成員
char *temp=new char[nSize]; //復(fù)制指針指向的內(nèi)容
memcpy(temp,RightSides.pBuffer,nSize*sizeof(char));
delete []pBuffer; //刪除原指針指向內(nèi)容 (將刪除操作放在后面,避免X=X特殊情況下,內(nèi)容的丟失)
pBuffer=NULL;
pBuffer=temp; //建立新指向
return *this
}

三、拷貝構(gòu)造函數(shù)使用賦值運(yùn)算符重載的代碼。
CExample::CExample(const CExample& RightSides)
{

*this=RightSides //調(diào)用重載后的"="
}
-----------------------------------------------

1-什么時(shí)候會(huì)用到拷貝構(gòu)造函數(shù)?
           當(dāng)任何你想復(fù)印東西的時(shí)候,而不管東西被復(fù)印成什么樣子。即任何你想利用一個(gè)已有的類實(shí)例給另一個(gè)類實(shí)例賦值時(shí),這種賦值可能是顯式的,也可能是隱式的
顯式:classa_1=class_2;
隱式:函數(shù)的形參有用到類對(duì)象卻沒有用引用或傳址技術(shù)時(shí)
               函數(shù)的返回值是一個(gè)對(duì)象也沒有應(yīng)用傳址技術(shù)時(shí)
2-什么時(shí)候有必要用拷貝構(gòu)造函數(shù)?
上述3種情況,如果沒有涉及到深拷貝問(wèn)題,就沒有必要自己來(lái)編寫拷貝構(gòu)造函數(shù),編譯器有默認(rèn)的可以很完美的完成任務(wù)
還一種情況就是變態(tài)的拷貝:在拷內(nèi)過(guò)程中進(jìn)行縮放,放大等處理,不過(guò)沒什么用

//關(guān)于深拷貝以下是引用:traceback:http://www./blog/user1/bachelor/archives/2006/375.html

關(guān)于深拷貝
如果一個(gè)類中含有指針成員變量,則在利用一個(gè)已存在對(duì)象構(gòu)造新的對(duì)象時(shí),就會(huì)面臨兩種選擇:深拷貝和淺拷貝。
       淺拷貝只是將對(duì)象間對(duì)應(yīng)的指針成員變量進(jìn)行簡(jiǎn)單的拷貝,即拷貝結(jié)束后新舊對(duì)象的指針指向相同的資源(指針的值是相同的);這種拷貝會(huì)導(dǎo)致對(duì)象的成員不可用,如下例:
        class Person
        {
           public :
           //....
           char * home;//the person's home
           void SetHome(char * str)
           {home = str;}
           ~Person()
            {
              //...
              delete [] home;
            }
        }
       //....
       char * place = new char [20];
       strcpy(place,"China");
       Person *A = new Person();
       A->SetHome(place);
   
       Person * B= Person(A);
       delete A;
      //....
      此時(shí)對(duì)象A 和對(duì)象B的成員home值相同,如果A對(duì)象 destroy,則對(duì)象B的成員home指向的地址變?yōu)椴豢捎茫▽?duì)象A撤銷時(shí)將home指向的資源釋放了)。
       深拷貝是相對(duì)于淺拷貝而言的,為了避免上述情況的發(fā)生,將上例中的代碼改造:對(duì)象拷貝時(shí)將指針指向的內(nèi)容拷貝,代碼如下:
      class Person
        {
           public :
           //....
           char * home;//the person's home
           void SetHome(char * str)
           {home = str;}
       
           Person & Person(const Person & per)
           {
                //...
                if(* this == per)//copy itself
        return *this;
                home = new char[strlen(per.home) +1];//alloc new memory
       strcpy(home,per.home);
       return * this;
           }
           ~Person()
            {
              //...
              delete [] home;
            }
        }
     深拷貝之后,新舊對(duì)象的home成員指向的內(nèi)容的值相同,而其自身的值不同。這樣就可避免出現(xiàn)其中之一
     destroy 之后,另一對(duì)象的home成員不可用。
     但是,此種開辟新的內(nèi)存區(qū)域然后賦值拷貝的方法在面臨指針成員指向的數(shù)據(jù)量很大時(shí)出現(xiàn)效率低下的問(wèn)題,因此又有下面的處理方法:為指針保留引用計(jì)數(shù)(reference counting)。
     當(dāng)類每次拷貝這個(gè)指針時(shí),就增加它的計(jì)數(shù);對(duì)象destroy時(shí)檢察指針的引用計(jì)數(shù),如果為0就刪除其指向的資源,否則計(jì)數(shù)減小。如果指針?biāo)笖?shù)據(jù)相對(duì)恒定是,指針引用計(jì)數(shù)尤為有效。而且在數(shù)據(jù)量龐大的時(shí)候,可以有較高的執(zhí)行效率。
      類示例代碼如下:

//一個(gè)引用計(jì)數(shù)器很容易作為一個(gè)類來(lái)完成
//用于帶一個(gè)引用計(jì)數(shù)值得void指針的類
class VoidRefPtr
{
public:
     //constructor allocates memory for data
     VoidRefPtr(void * pData,int nSize)
     {
      ptr = malloc(nSize);
      memcpy(ptr,pData,nSize);
      nCount = 1;
     }

     //increment the reference count
     VoidRefPtr * Ref()
     {
      nCount ++;
      return this;
     }

     //decrement the count and delete if zero
     void Derf()
     {
      if(!--nCount)
       delete this;
     }

     ~VoidRefPtr()
     {
      delete ptr
     }

     void * ptr()
     {
      return ptr
     }
private:
     void * ptr;//the data
     unsigned int nCount;//the reference count

};

//這個(gè)類包含了一個(gè)實(shí)際的指針和用于對(duì)引用計(jì)數(shù)器自加自減的成員函數(shù)
//當(dāng)引用計(jì)數(shù)器減到零時(shí),對(duì)象撤銷自身。
class RefcountCharPtr
{
public:
RefcountCharPtr(const char * pData ="")
{
     //constructor creates a ref pointer
     pRef = new VoidRefPtr((void *)pData,strlen(pData)+1);
}

RefcountCharPtr(const RefcountCharPtr &a)
{
     //copy constructor increments ref count
     pRef = a.pRef->Ref();
}

~RefcountCharPtr()
{
     //destructor decrements ref count
     pRef ->Derf();
}

const RefcountCharPtr & operator = (const RefcountCharPtr & a)
{
     //asignment
     //decrement ref count of existing pointer
     pRef->Derf();
     //increment that of assigned pointer
     pRef = a.pRef->Ref();
     return * this;
}

RefcountCharPtr & operator =(const char * pData)
{
     //char string assignment
     //decrement ref count of existing pointer
     pRef->Derf();
     //make new pointer
     pRef = new VoidRefPtr((void *)pData,strlen(pData)+1);
     return *this;
}

//type conversion to a char string
operator const char * ()
{
     return (char * )pRef->ptr();
}

protected:
private:
VoidRefPtr * pRef;
};
     一個(gè)同學(xué)給出了一個(gè)類似的實(shí)現(xiàn):
      class RefData
{
public:
     RefData(char *s)
     {
      data = new String(s);
     }
int refCount;
String *data;
};

class MyString
{
public:
     MyString(const char *s = "");
//...
//...
private:
     RefData * value; // 私有指針變量
};

MyString::MyString(const char *s = ""):value(new RefData(s))
{
}

MyString & MyString::operator=(const MyString & str)
{
if (value == str.value) //如果是自己復(fù)制給自己
     return *this;
if (--(value->refCount) == 0)
     delete value;
value = s.value; // 只拷貝指針
++(value->refCount);
return *this;
}

char & MyString::operator[](int dex) //改變值的時(shí)候
{
if (value->refCount >1)
{
     --value->refCount;
     value = new RefData(value->data)
}
return value->data[index];
}

//測(cè)試示例
int main()
{
MyString s1 ="hello";
char *p = &s1[1];
MyString s2 = s1;
*p = 'x';
}

     雖然引用計(jì)數(shù)的處理方法貌似不錯(cuò),但是其亦存在其弊端:在多線程的條件下,其執(zhí)行效率尤為低下。具體參見(Herb Shutter的在C++ Report雜志中的Guru專欄以及整理后出版的《More Exceptional C++》中專門的討論)。
    
     最近在看設(shè)計(jì)模式的一些東東,偶然又發(fā)現(xiàn)了下面的一段:
     下面是一些可以使用Proxy模式的常見情況:
     ...
     4)智能指引(smart reference)--取代了簡(jiǎn)單的指針,它在訪問(wèn)對(duì)象時(shí)執(zhí)行一些附加操作。它的典型用途包括:
      對(duì)指向?qū)嶋H對(duì)象的引用計(jì)數(shù),這樣當(dāng)該對(duì)象沒有引用時(shí),可以自動(dòng)釋放它(也稱為 smartpointers[Ede92]).
      當(dāng)?shù)谝淮我靡粋€(gè)持久對(duì)象時(shí),將它裝入內(nèi)存。
      在訪問(wèn)一個(gè)實(shí)際對(duì)象前,檢察是否已經(jīng)鎖定了它,以確保其他對(duì)象不能改變它。
      以上的敘述的用途好像有些像引用計(jì)數(shù),但是具體如何,現(xiàn)在偶還不是很清楚,準(zhǔn)備將兩者放在一起,看其聯(lián)系和區(qū)別究竟如何。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多