真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

通過(guò)STL中的string看寫(xiě)時(shí)拷貝和讀時(shí)拷貝

   之前的博客已經(jīng)給出了如何自己定義一個(gè)string類(lèi),以及其內(nèi)部應(yīng)該有的操作,今天就讓我們根據(jù)STL庫(kù)中給出的string來(lái)看看,它重要的寫(xiě)實(shí)拷貝實(shí)現(xiàn)和一點(diǎn)點(diǎn)讀時(shí)拷貝(也是寫(xiě)時(shí)拷貝)

創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:做網(wǎng)站、成都網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿(mǎn)足客戶(hù)于互聯(lián)網(wǎng)時(shí)代的社旗網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!


1、寫(xiě)時(shí)拷貝(copy-on-write)

class String
{
 public:
 String(const String &str)
 :_pData(NULL)
 {
     String temp(str._pData);
     swap(_pData,temp._pData);
 }
 private:
 char *_pData;
}
void test()
{
    String s1("hello world");//構(gòu)造
    String s2(s1);//拷貝構(gòu)造
}

這里面實(shí)現(xiàn)的是用空間換時(shí)間的一種方法,定義極其簡(jiǎn)單,然而大神們寫(xiě)出來(lái)的STL庫(kù)中的string是更為精巧的。就是應(yīng)用了寫(xiě)實(shí)拷貝的技術(shù),防止淺拷貝發(fā)生,并且還省了空間,通過(guò)STL中的string看寫(xiě)時(shí)拷貝和讀時(shí)拷貝

那么問(wèn)題來(lái)了????

Q:什么是寫(xiě)時(shí)拷貝呢?

A:寫(xiě)時(shí)拷貝就是一種拖延戰(zhàn)術(shù),當(dāng)你真正用到的時(shí)候才去給它開(kāi)辟空間,不然它只是看起來(lái)存在,實(shí)際上只是邏輯上的存在,這種方法在STL的string中體現(xiàn)的很明顯。

由于string類(lèi)是用char* 實(shí)現(xiàn)的,其內(nèi)存都是在堆上開(kāi)辟和釋放的。堆上的空間利用要很小心,所以當(dāng)你定義一個(gè)sring類(lèi)的對(duì)象,并且想對(duì)這個(gè)對(duì)象求地址,其返回值是const char*類(lèi)型,onlyread屬性哦,如果還想對(duì)該地址的內(nèi)容做什么改變,只能通過(guò)string給的方法去修改。

舉個(gè)栗子:

#include 
#include 
using namespace std;
int main()
{
    string s1("再來(lái)一遍:hello world");
    string s2(s1);
    //c++方式打印一個(gè)字符串的地址!?。。?!
    //static_cast---c++中的強(qiáng)制類(lèi)型轉(zhuǎn)換,不檢查
    //string的c_str()方法返回值是const char *
    cout<(s1.c_str())<(s2.c_str())<_<)~~~~我也這么覺(jué)得
    printf("%x\n",s1.c_str());
    printf("%x\n",s2.c_str());
}

(vs2010版本)

結(jié)果是不是和你想的不一樣。。。。(明明應(yīng)該不變的說(shuō)~)

vc 6.0版本下:s1,s2的地址是一樣的。這里就不進(jìn)行截屏了,如果有興趣的同學(xué),下去可以試試哈~

那么當(dāng)對(duì)s1,s2進(jìn)行修改時(shí)是怎么樣的呢

s1[0]='h';
s2[0]='w';

(vs2010版本)

VC6.0版本下:s1,s2的地址不一樣(同vs2010版本)

所以我們得出的結(jié)論是:

當(dāng)對(duì)string對(duì)象只進(jìn)行拷貝構(gòu)造時(shí),發(fā)生的是寫(xiě)時(shí)拷貝(假拷貝),只有對(duì)其對(duì)象進(jìn)行修改時(shí)(有寫(xiě)的操作),才對(duì)其對(duì)象另外開(kāi)辟空間,進(jìn)行修改。

要想達(dá)到這樣的效果,在一定程度上節(jié)省了空間。

必須做到兩點(diǎn):內(nèi)存的共享,寫(xiě)時(shí)拷貝。

(1)copy-on-write的原理?

         “引用計(jì)數(shù)”,程序猿就是這般機(jī)智~~~~

         當(dāng)對(duì)象s1實(shí)例化,調(diào)用構(gòu)造,引用計(jì)數(shù)初始化=1;

         當(dāng)有對(duì)象對(duì)s1進(jìn)行拷貝時(shí),s1的引用計(jì)數(shù)+1;

         當(dāng)有對(duì)象是由s1拷貝來(lái)的或者是s1自身進(jìn)行析構(gòu)是,s1的引用計(jì)數(shù)進(jìn)行-1;

         當(dāng)有對(duì)象是由s1拷貝來(lái)的或者是s1自身需要修改時(shí),進(jìn)行真拷貝,并且引用計(jì)數(shù)-1;

         當(dāng)引用計(jì)數(shù)==0的時(shí)候,進(jìn)行真正的析構(gòu)。

(2)引用計(jì)數(shù)應(yīng)該如何設(shè)計(jì)在?

            關(guān)于引用計(jì)數(shù)的實(shí)現(xiàn),你是不是也有這樣的疑惑呢?

            當(dāng)類(lèi)的對(duì)象之間進(jìn)行共享時(shí),引用計(jì)數(shù)也是共享的

            當(dāng)類(lèi)中的對(duì)象從公共中脫離出來(lái),引用計(jì)數(shù)就是它自己的了。

            那么如何做到從獨(dú)立--->共享--->獨(dú)立的呢???

            如果你想將引用計(jì)數(shù)當(dāng)做String類(lèi)的成員變量,那么什么樣的類(lèi)型適合它呢?

             int _count;  那么每個(gè)對(duì)象的實(shí)例化都擁有一個(gè)自己的引用計(jì)數(shù),無(wú)法實(shí)現(xiàn)共享

class String
{
    public:
        String(pData=NULL)
        :_pData(new char[strlen(pData)+1])
        ,_count(1)
        {
            strcpy(_pData,pData);
        }
        ~String()
        {
            if(--_count==0)
            {
                delete []_pData;
            }
        }
        String(String &str)
        :_pData(str._pData)
        {
            str._count++;
            _count=str._count;
        }
    private:
        char *_pData;
        int _count;
};

string s1="hello world";
string s2(s1);

//s1構(gòu)造,s2拷貝構(gòu)造:s1和s2指向同一空間,s1和s2的_count都變成2
//當(dāng)s2先析構(gòu),s2的_count--變成1,不釋放
//當(dāng)s1析構(gòu)時(shí),s1的_count--變成1,不釋放
//造成內(nèi)存泄露

             static int _pCount;那么每個(gè)對(duì)象的實(shí)例化都擁有這唯一的一個(gè)引用計(jì)數(shù),共享范圍過(guò)大   

class String
{
    public:
        String(pData=NULL)
        :_pData(new char[strlen(pData)+1])
        {
            _count=1;
            strcpy(_pData,pData);
        }
        ~String()
        {
            if(--_count==0)
            {
                delete []_pData;
            }
        }
        String(String &str) //不加const,不然底下的淺拷貝會(huì)出錯(cuò)
        :_pData(str._pData)
        {
            str._count++;
        }
    private:
        char *_pData;
        static int _count;  //靜態(tài)的成員變量要在類(lèi)外進(jìn)行初始化
};
int String::_count=0;

string s1="hello world";
string s2(s1);
string s3("error");

//s1構(gòu)造,s2拷貝構(gòu)造:s1和s2指向同一空間,_count都變成2
//s3構(gòu)造,_count變成1
//當(dāng)s3先析構(gòu),_count--變成0,釋放
//s1,s2造成內(nèi)存泄露

int *_pCount;可以實(shí)現(xiàn)引用計(jì)數(shù)。

class String
{
    public:
        String(pData=NULL)
        :_pData(new char[strlen(pData)+1])
        ,_pCount(new int(1))
        {
            strcpy(_pData,pData);
        }
        ~String()
        {
            if(--(*_pCount)==0)
            {
                delete []_pData;
                delete _pCount;
            }
        }
        String& operator=(const String *str)
        {
            if(_pData!=str._pData)
            {
                if(--(*_pCount)==0)
                {
                    delete _pCount;
                    delete []_pData;
                }
                (*str._pCount)++;
                _pCount=str._pCount;
                _pData=str._pData;
            }
            return *this;
        }
        String(String &str) //不加const,不然底下的淺拷貝會(huì)出錯(cuò)
        :_pData(str._pData)
        ,_pCount(str._pCount)
        {
            (*str._pCount)++;
        }
    private:
        char *_pData;
        int *_pCount;  
};

這些字符串都是在堆上開(kāi)辟的,那么引用計(jì)數(shù)也可以在堆上開(kāi)辟,要從邏輯上,看引用計(jì)數(shù)是個(gè)指針,存次數(shù),從物理上看,引用計(jì)數(shù)應(yīng)該和字符指針?lè)旁谝黄穑阌诠芾?。讓?shù)據(jù)相同的對(duì)象都可以共享同一片內(nèi)存。

綜上,引用計(jì)數(shù)的設(shè)計(jì)如圖:通過(guò)STL中的string看寫(xiě)時(shí)拷貝和讀時(shí)拷貝

(3)引用計(jì)數(shù)什么時(shí)候需要共享呢?

          情況1:string s2(s1);     //s2拷貝自s1,即s2中的數(shù)據(jù)和s1的一樣

          情況2:string s2; s2=s1;//s2的數(shù)據(jù)由s1賦值而來(lái),即s2中的數(shù)據(jù)和s1的一樣

     綜上所述:

            string類(lèi)中的拷貝構(gòu)造和賦值運(yùn)算符重載需要引用計(jì)數(shù)

 (4)什么情況下需要進(jìn)行寫(xiě)時(shí)拷貝

           對(duì)內(nèi)容有修改時(shí)

 (5)c++版實(shí)現(xiàn)代碼

class String
{    
    private:
        char *_pData;  //引用計(jì)數(shù)存在于_pData[-1]
    public:
        //構(gòu)造函數(shù)
        String(pData=NULL)
        :_pData(new char[strlen(pData)+1+sizeof(int)])
        {
              //強(qiáng)轉(zhuǎn)在頭上4個(gè)字節(jié)存放引用計(jì)數(shù)的值
               (*(int *)_pData)=1;
              //恢復(fù)其字符串的長(zhǎng)度
              _pData+=4; 
              strcpy(_pData,pData);
        }
        //拷貝構(gòu)造
        String(const String&str)
        :_pData(str. _pData)
        {
           
           //(*(--(int *)str._pData)) ++;  
           //這個(gè)版本是錯(cuò)的,大家看看錯(cuò)在哪里?可以留言告訴我哦
            (*(--(int*)_pData-1)++;
        }
        //賦值運(yùn)算符重載
        String& operator=(const String &str)
        {
            if(_pData!=str._pData)
            {
                if(--(*(--(int*)_pData-1)==0)
                {
                    _pData-=4;
                    delete []_pData;
                }
                
                else
                {
                    _pData=str._pData;
                    (*(--(int*)_pData-1)++;
                }
            }
            return *this;
        }
        //析構(gòu)函數(shù)
        ~String()
        {
            if(--(*(--(int*)_pData-1)==0)
                {
                    _pData-=4;
                    delete []_pData;
                }  
        }  
};

2、讀時(shí)拷貝(copy-on-read)

當(dāng)C++的STL庫(kù)中的string被這么利用時(shí):

string s1="hello world";
long begin =  getcurrenttick();
for(size_t i=0;i



網(wǎng)站名稱(chēng):通過(guò)STL中的string看寫(xiě)時(shí)拷貝和讀時(shí)拷貝
URL鏈接:http://weahome.cn/article/jpgois.html

其他資訊

在線(xiàn)咨詢(xún)

微信咨詢(xún)

電話(huà)咨詢(xún)

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部