C++的std::string的“讀時也拷貝”技術!

C++的std::string的讀時也拷貝技術!

嘿嘿,你沒有看錯,我也沒有寫錯,是讀時也拷貝技術。什么?我的錯,你之前聽說寫過時才拷貝,嗯,不錯的確有這門技術,英文是Copy On Write,簡寫就是COW,非?!!?!那么我們就來看看這個’?!夹g的效果吧。

我們先編寫一段程序

#include <string>
#include <iostream>
#include <sys/time.h>
static long getcurrenttick()
{
    long tick ;
    struct timeval time_val;
    gettimeofday(&time_val , NULL);
    tick = time_val.tv_sec * 1000 + time_val.tv_usec / 1000 ;
    return tick;
}
int main( )
{
    string the_base(1024 * 1024 * 10, 'x');
    long begin =  getcurrenttick();
    for( int i = 0 ;i< 100 ;++i ) {
       string the_copy = the_base ;
    }
    fprintf(stdout,"耗時[%d] \n",getcurrenttick() - begin );
}

嗯,一個非常大的字符串,有10M字節的x,并且執行了100此拷貝。編譯執行它,非??欤谖业奶摂M機甚至不要1個毫秒。

現在我們來對這個string加點料!

int main(void) {
    string the_base(1024 * 1024 * 10, 'x');
    long begin =  getcurrenttick();
    for (int i = 0; i < 100; i++) {
        string the_copy = the_base;
        the_copy[0] = 'y';
    }
    fprintf(stdout,"耗時[%d] \n",getcurrenttick() - begin );
}

現在我們再編譯并執行這斷程序,居然需要4~5秒!哇!非常美妙的寫時才拷貝技術,性能和功能的完美統一。

我們再來看看另外一種情況!

string original = "hello";
char & ref = original[0];
string clone = original;
ref = 'y';

我們生成了一個string,并保留了它首字符的引用,然后復制這個string,修改string中的首字符。因為寫操作只是直接的修改了內存中的指定位置,這個string就根本不能感知到有寫發生,如果寫時才拷貝是不成熟的,那么我們將同時會修改original和clone兩個string。那豈不是災難性的結果?幸好上述問題不會發生。clone的值肯定是沒有被修改的。看來COW就是非常的牛!

以上都證明了我們的COW技術非常牛!

有太陽就有黑暗,這句說是不是有點耳熟?

int main(void) {
    string the_base(1024 * 1024 * 10, 'x');
    fprintf(stdout,"the_base's first char is [%c]\n",the_base[0] );
    long begin =  getcurrenttick();
    for (int i = 0; i < 100; i++) {
        string the_copy = the_base;
    }
    fprintf(stdout,"耗時[%d] \n",getcurrenttick() - begin );
}

啊,居然也是4~5秒!你可能在想,我只是做了一個讀,沒有寫嘛,這到底是怎么回事?難道還有讀時也拷貝的技術!。

不錯,為了避免了你通過[]操作符獲取string內部指針而直接修改字符串的內容,在你使用了the_base[0]后,這個字符串的寫時才拷貝技術就失效了。

C++標準的確就是這樣的,C++標準認為,當你通過迭代器或[]獲取到string的內部地址的時候,string并不知道你將是要讀還是要寫。這是它無法確定,為此,當你獲取到內部引用后,為了避免不能捕獲你的寫操作,它在此時廢止了寫時才拷貝技術!

這樣看來我們在使用COW的時候,一定要注意,如果你不需要對string的內部進行修改,那你就千萬不要使用通過[]操作符和迭代器去獲取字符串的內部地址引用,如果你一定要這么做,那么你就必須要付出代價。當然,string還提供了一些使迭代器和引用失效的方法。比如說push_back,等, 你在使用[]之后再使用迭代器之后,引用就有可能失效了。那么你又回到了COW的世界!比如下面的一個例子

int main( )
{
    struct timeval time_val;
    string the_base(1024 * 1024 * 10, 'x');
    long begin = 0 ;
    fprintf(stdout,"the_base's first char is [%c]\n",the_base[0] );
    the_base.push_back('y');
    begin = getcurrenttick();
    for( int i = 0 ;i< 100 ;++i ) {
        string the_copy = the_base ;
    }
    fprintf(stdout,"耗時[%d] \n",getcurrenttick() - begin );
}

一切又恢復了正常!如果對[]返回引用進行了操作又會發生情況呢,有興趣的朋友可以試試!結果非常令人驚訝。

另外:上述例子是在linux環境下編譯的,使用STL是GNU的STL。windows上我用的是vs2003,但是非常明顯vs2003一點都不支持COW。

這篇文章出自http://ridiculousfish.com/blog/archives/2009/09/17/i-didnt-order-that-so-why-is-it-on-my-bill-episode-2/ 這里,我使用了它的例子。但是我重新自己組織了內容。

編寫這篇文章的同時,我還參考了耗子的《標準C++類string的Copy-On-Write技術》一文

轉自:http://coolshell.cn/articles/1443.html

原創文章,作者:s19930811,如若轉載,請注明出處:http://www.www58058.com/2430

(0)
s19930811s19930811
上一篇 2015-04-03 21:59
下一篇 2015-04-03 22:07

相關推薦

  • 第四周作業

    1、復制/etc/skel目錄為/home/tuser1,要求/home/tuser1及其內部文件的屬組和其它用戶均沒有任何訪問權限。 [root@localhost ~]# cp /etc/skel /home/tuser1 [root@localhost ~]# chmod -R&nb…

    Linux干貨 2017-01-18
  • Vim 末行模式 & crontab & scripts 練習

    1、復制/etc/rc.d/rc.sysinit文件至/tmp目錄,將/tmp/rc.sysinit文件中的以至少一個空白字符開頭的行的行首加#;   ~]# cp /etc/rc.d/rc.sysinit /tmp     %s@^[[:space:]]\+\*@#&@g     (使用元字符 有幾…

    Linux干貨 2016-10-31
  • linux 目錄結構

    該文章主要來自于網絡資料進行整理 目錄結構參考地址: http://www.iteye.com/topic/1125162 http://yangrong.blog.51cto.com/6945369/1288072 http://itlab.idcquan.com/linux/administer/939529_1.html http://itlab.id…

    系統運維 2015-12-19
  • 馬哥教育21期網絡班—第12周課程+練習—-LAMP練習

    1、請描述一次完整的http請求處理過程; (1) 建立或處理連接:接收請求或拒絕請求 (2) 接收請求: 接收來自于網絡的請求報文中對某資源的一次請求的過程; 持久連接:接收到請求不會斷開這個請求 非持久連接:一個連接請求斷開一次 并發訪問響應模型(Web I/O): 單進程I/O結構:啟動一個進程處理用戶請求,而且一次只處理…

    Linux干貨 2016-09-26
  • 馬哥教育網絡班22期+第二周課程練習

    Linux上文件可管理類命令,其常用的使用方法和相關示例 目錄管理類命令 目錄管理類命令包含:mkdir,rmdir mkdir 使用說明及格式 移除空目錄 rmdir [OPTION]… DIRECTORY…   常見選項及說明 -p –parents 創建時如果父目錄不存在,一并創建 -v –ve…

    Linux干貨 2016-08-24
欧美性久久久久