青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

清風竹林

ぷ雪飄絳梅映殘紅
   ぷ花舞霜飛映蒼松
     ----- Do more,suffer less

Solmyr 的小品文系列之八:拷貝

“zero 幫幫忙吧 ~~ ”

“燦爛”的笑臉,充滿誠意的眼神,再加上點頭哈腰的姿勢,這三者構成了一尊名為“有求于人”的塑像。

在 QQ 上聊的正歡的 zero 抬起頭,看著塑像的作者和材料 ——— pisces ,方圓五十米內唯一的女性程序員 ——— 問道:“什么事?”

“我這里有一段 C++ 程序調不通。”

“這類問題你應該去問 Solmyr。”

“哎呀,別開玩笑了,我哪敢去問他呀!總說我笨!上次問他一個小問題,結果又被訓的狗血噴頭,哼!”,pisces 顯得忿忿不平,“還是你來幫幫我吧,我知道你是部門里有數的高手,肯定搞的定的。幫幫忙吧 ~~”

zero 明顯的被打動了,于是,在 pisces 的努力下,zero 坐到了 pisces 的計算機前。

“好吧,什么問題?”

“是這樣的啦,這里有一組 C 風格的 API ,負責管理設備上的字符通信鏈接。它們是好些年前設計的”,說著,pisces 調出了一些代碼:

// old C style API
typedef int conn_handle;
typedef struct
{
   /* ... 打開鏈接所需的參數和屬性 ... */
}conn_attr;

conn_handle open_conn(conn_attr* p_attr, char* buf, unsigned int buf_size);
void close_conn(conn_handle h);

char read_conn(conn_handle h);
void write_conn(conn_handle h, char c);

...

“枝節的東西不算,主干大概就是這樣,一對函數負責打開和關閉,一對函數負責讀寫。創建鏈接時候的那個 buf 參數指向一個緩沖區,這個要你自己分配并把長度傳進去,和鏈接一一對應,read_conn/write_conn 會用它做緩沖。我的任務就是寫個類把這些 API 包裝起來。”,說著 pisces 又調出了另外一段代碼:

// pisces' connection class
class connection
{
private:
   conn_attr m_attr;
   bool m_opened;
   int m_bufsize;
   char* m_buf;

   conn_handle m_h;
   ...

public:
   connection(const conn_attr& attr, int bufsize)
   {
       m_attr = attr;
       m_opened = false;
       m_bufsize = bufsize;
       m_buf = new char[m_bufsize];
   }
   ~connection() { delete m_buf; }

   void open()
   {
       m_h = open_conn(&m_attr, m_buf, m_bufsize);
       m_opened = true;
   }
   void close()
   {
       close_conn(m_h);
       m_opened = false;
   }

   char read()
   {
       assert(m_opened);
       return read_conn(m_h);
   }
   void write(char c)
   {
       assert(m_opened);
       write_conn(m_h, c);
   }
   ...

};

“應該是很簡單的,可是不知道怎么回事,用了 connection 類的程序總是時不時的崩潰,說是非法的內存操作。”,pisces 顯得很苦惱。

zero 一眼就看出了毛病 ——— 這使他小小的自鳴得意了一下 ——— 但是表面上不動聲色,等到他看過 pisces 提供的“總是引發崩潰”的代碼段之后,他才開口說到:

“這 是一個常見的錯誤 pisces”,zero 盡量使自己的口吻和語氣聽起來象一個權威,“關于 C++,有一條重要的指導原則:析構函數、拷貝構造函數和賦值運算符三者幾乎總是一起出現。也就是說,如果你為一個類寫了析構函數,那么往往你不得不再提 供一個拷貝構造函數和一個賦值運算符,違反它往往意味著錯誤。你看這里:”

說著,zero 在屏幕上標出了兩行代碼:

void some_func()
{
   conn_attr attr;
   ...
   connection c1(512, attr);
   connection tmp = c1;
   ...
}

“這 里對象 tmp 是從 c1 拷貝構造而來的,而你沒有定義拷貝構造函數,這使得編譯器在這里自動進行按位拷貝,而這使得 tmp 和 c1 的所有成員都相等,包括 m_buf 成員。這樣在函數返回時,c1 析構的時候 delete 了一遍 m_buf,在 tmp 析構的時候又 delete 了一遍 ……”

“哦!我明白了!” pisces 打斷了 zero ,“所以就出現一個非法內存操作,對吧?哎呀,這一條以前在學校里寫 string 類的時候遇到過,我怎么會忘了呢?”

“對,你只要寫一個拷貝構造函數和一個賦值運算符處理一下 m_buf 指針就可以解決這個問題了。這你自己搞的定吧?”

“我可以的,多謝了 zero !”

zero 心滿意足的回到了自己的座位上,開始繼續和“你不懂我纖細的心”在 QQ 上探討“愛情的意義”。可是好景不長,沒過多久,本文開頭所描述的景象再一次的出現了。

“zero 幫幫忙吧 ~~ ”

zero 在心中嘆了口氣,抬頭問道:“又是什么問題,pisces?”

“呃,還是那個類。我照你說的給 conn 添加了拷貝構造函數,非法內存操作確實少多了,可還是有,還有好像鏈接傳輸數據也有點問題 ———— 你還是過來幫我看看吧 ~~”

zero 心不甘情不愿的再次來到了 pisces 的計算機前,翻出 pisces 寫的拷貝構造函數檢查起來:

connection(const connection& other)
{
   m_attr = other.m_attr;
   m_bufsize = other.m_bufsize;
   m_buf = new char[m_bufsize];
   memcpy(m_buf, other.m_buf, m_bufsize);
   m_opened = other.m_opened;

   m_h = other.m_h;
}

zero 的眉頭皺了起來,這個拷貝構造函數似乎應該可以解決問題,顯然現在兩個 m_buf 各自指向合法的內存,不再存在兩次釋放的問題。那么問題出在哪兒呢?zero 陷入了沉思。不過僅僅多花了不到 1 分鐘,zero 就明白了過來。

“哦!我明白了!見鬼,我怎么會沒注意到這個。pisces ,問題還是出在 m_buf 上面,因為鏈接和緩沖區指針是一一對應的,所以拷貝構造函數里新分配的緩沖區根本不起作用。”

pisces 眨了眨眼,表情略顯呆滯。

“給你舉個例子吧。”zero 飛快的鍵入一段測試代碼:

connection* pc = NULL;

{
   conn_attr attr;
   connection c1(512, attr);
   c1.open();
   pc = new connection(c1);
}

pc->write('A');

“c1 的構造函數里調用 new 為它的 m_buf 成員分配內存,緊接著在 open 函數里調用 open_conn 打開了一個鏈接,注意這里我們傳入 open_conn 的參數是 c1.m_buf ,所以這個鏈接對應的緩沖區指針是 c1.m_buf 。然后我們執行 pc = new connection(c1),新對象從 c1 拷貝構造,所以 pc->m_h 和 c1.m_h 相等,也就是說這兩個對象保存的 m_h 標識著同一個鏈接,對應的緩沖區指針都是 c1.m_buf ———— ”

zero 象 Solmyr 常做的那樣停了下來,但卻失望的看到 pisces 毫無反應,只好接著往下說:

“所 以接下來的 pc->write 在調用 write_conn 時候,這個 API 并不知道這是通過另外一個對象在調用它,它仍然試圖使用 c1.m_buf 作為緩沖區,但這個時候 c1 已經結束了它的生命周期,c1.m_buf 已經被釋放了,所以,這是一個非法的內存訪問。”

pisces 舔了舔嘴唇:“ …… 那 …… 那么現在怎么辦?”

zero 翻了個白眼 ——— 很明顯 pisces 根本沒明白是怎么一回事 ——— 開始考慮怎樣應付眼前這個問題。

“嗯, 看樣子,這里必須考慮多個對象共享一個指針的問題,嗯,為了保證這塊內存被釋放 …… 恐怕 …… 恐怕得用上引用計數技術(請參見“小品文系列之五:垃圾收集”)才搞得定,要不要用 boost::shared_ptr 呢?”,zero 一邊想,一邊自言自語。突然間 ———

“邏輯的混亂導致實現上的復雜,zero,這個 connection 類千瘡百孔啊。”,Solmyr 的聲音毫無預兆的在背后響起。

zero 在 0.01 秒內控制住了拔腿飛奔的沖動,以盡可能放松的姿態緩緩的轉過身來。在他的面前是披著一貫優雅偽裝的 Solmyr,一手端著果汁,一手牢牢的拽著仍在拼命掙扎試圖逃走的 pisces 。

“啊 Solmyr ,我正想找你呢,這個問題稍許有點棘手。”

“是嗎?那你的腿為什么在抖?”

“嗯?沒有,有點冷而已 …… 啊 Solmyr ,你剛剛說什么來著?”

“邏輯的混亂導致實現上的復雜,zero,這個 connection 類千瘡百孔。”Solmyr 把 pisces 按在旁邊的座位上,接著說到:“你剛才發現的問題只是其中之一而已。看一下這個:”

void some_func()
{
   conn_attr attr;
   ...
   connection c1(512, attr);
   c1.open();
   ...
   connection tmp = c1;
   c1.close();
   tmp.write('a');
   ...
}

“這會導致什么?”

“ …… 試圖寫入一個已經關閉了鏈接。”

“還需要我給出多次打開一個鏈接,多次關閉一個鏈接,以及各種鏈接處于打開狀態但讀寫卻會引發斷言錯誤的例子嗎?”

“ …… 不用了。”

“那你打算怎樣修復這些問題?要不要在每個對象里保存一個由它拷貝構造而來的對象列表?或者你打算在文檔里寫‘以下 371 種方式使用該類會導致無法預知的錯誤’?”

“ …… ”

Solmyr 重重的嘆了口氣:“你被 pisces 誤導了,zero,因為你只想著怎么幫 pisces 解決問題,如果一開始就讓你來設計這個類,情況一定不會這么糟糕。”說著,Solmyr 狠狠的瞪了 pisces 一眼。“不要忘了,C++ 類不是簡單的把一堆成員變量和成員函數湊在一起,永遠記得這個原則:C++ 中用類來表示概念。”

zero 點了點頭。

“我來問你,connection 這個類應該表示什么概念?”

“呃,應該表示‘鏈接’這個概念。”

“一個 connection 類的對象應該代表 ……”

“應該代表一個實際‘鏈接’。”

“很好。那么你告訴我,你剛才努力想設計出的那個拷貝構造函數要干什么?”

“ …… 讓兩個 connection 對象能夠表示同一鏈接。”

“所以 ……”

“ …… 所以 …… 嗯 …… 哦 …… ”zero 露出了恍然大悟的表情:“所以我實際上想做的是要表達這樣一個概念:如果一個 connection 對象沒有被拷貝,它就表示一個獨立的鏈接,如果它被拷貝了,那么它就和拷貝者表示同一個鏈接,這也包括拷貝者的拷貝者,拷貝者的拷貝者的拷貝者 …… 天哪,這根本是一團亂麻!”

“對,問題就在這里。一個 connection 對象代表什么?你試圖給出一個在邏輯上非常混亂的答案,這導致了實現的復雜性。實際上,如果理清這個邏輯,問題是很簡單的:一個 connection 對象代表一個鏈接,它構造,代表建立了一個鏈接;它析構,代表這個鏈接走完了它的生命歷程 ——— 這里 open 和 close 這兩個成員函數根本就是多余的。至于拷貝構造 ……”

Solmyr 頓了頓,以一種斬釘截鐵式的語氣說到:

“應該禁止。”

“禁止拷貝?!”

“對, 應該禁止。事實上,對于‘鏈接’這個概念而言,‘拷貝’動作含義模糊:拷貝意味著什么?拷貝構造的對象所表示的鏈接和原來的鏈接是什么關系?當使用 connection 類的程序員看到 connection c2 = c1; 這樣的代碼時,他沒法從代碼本身看出這是什么意思,他會猜測,c1 和 c2 代表的是一個鏈接?還是兩個鏈接?只能通過查閱文檔來解決,這加重了使用者的負擔,而如果禁止拷貝,所有智力正常的程序員都會明白每個 connection 對象唯一的代表一個鏈接。”

zero 若有所思的點了點頭。

“同時,這還能阻止程序員用傳值方式向函數傳遞 connection 對象 ——— 想象一下,如果一個程序員這樣使用 connection ,會發生什么?”,Solmyr 鍵入了下面的代碼:

void send_a_greeting(connection c)
{
   c.write("Hello!");
}

zero 沒費什么勁就看出了問題:“函數的設計者以為他是在向調用者傳入的鏈接發送消息,但實際上這個函數在按值傳遞參數的時候創建了一個新鏈接。”

Solmyr 點了點頭,繼續說到:“還有,從擴展性的角度考慮,也應該禁止拷貝。比如,假設你將來打算控制鏈接的創建,把創建過程封裝起來,那么這個拷貝構造函數就在 你的封裝上捅了一個大窟窿 ——— 每個人都可以很方便的利用拷貝構造任意創建鏈接;又比如,假設將來你需要支持多個類型的鏈接,要把 connection 作為一個類層次的接口基類,那時,connection 的拷貝構造就必須要禁止,而你之前支持拷貝構造帶來的代價就是辛苦的翻遍之前所有的代碼去掉所有拷貝構造。”

“那,如果我確實需要在多處訪問一個鏈接,該 ……” zero 沒等 Solmyr 回答,自己就接了上去,“呃,也很簡單,只要傳遞引用就可以了,或者如果需要更好的控制,可以用智能指針什么的。”

“完 全正確。說起來,其實許多類 ——— 比許多人所認為的要多的多 ——— 所表示的概念對于‘拷貝’這個動作都沒有清楚的定義,比如常見的‘窗口’、‘文件’、‘事務’等等等等,禁止它們拷貝往往可以讓代碼的邏輯清楚許多。以后 你在設計類的時候,完全可以首先考慮是否禁止它的拷貝構造,如果不能禁止,再去考慮怎么寫拷貝構造函數的問題。好了 zero ,現在你能給出 connection 的實現嗎?”

“Sure!只要將拷貝構造函數和重載賦值運算符設為私有,就可以禁止拷貝了。”zero 拖過鍵盤,三兩下屏幕上就出現了一個新的實現:

class connection
{
private:
   conn_attr m_attr;
   int m_bufsize;
   char* m_buf;

   conn_handle m_h;
   ...

public:
   connection(const conn_attr& attr, int bufsize)
   : m_attr(attr), m_bufsize(bufsize)
   {
       m_buf = new char[m_bufsize];
       m_h = open_conn(&m_attr, m_buf, m_bufsize);
   }
   ~connection()
   {
       close_conn(m_h);
       delete m_buf;
   }

   void write(char c){ write_conn(m_h, c); }
   char read(){ return read_conn(m_h); }
   ...

private:
   connection(const connection&);
   connection& operator=(const connection&);
};

“嗯,很好,這個問題可以告一段落了。”Solmyr 點了點頭,準備離開,但又停了下來:“對了 zero ,pisces 他們這邊曾經打報告要求增加人手,從今天的情況來看也確實需要有個懂點 C++ 的人加強這邊。我看你正好有空,這個事就你來負責吧。”

zero 心中暗暗叫苦,趕緊分辨:“沒有啊 Solmyr,我現在手邊的事情多得做不完啊!”

“是嗎?哦 …… 對了,我剛才接到網管的報告,說有個人的電腦最近頻繁的訪問 QQ 的服務器,那個人是誰來著?”,Solmyr 又露出了他招牌式的微笑。

“呃 …… 我又想了想,雖然我確實事情比較多,但團隊合作精神還是要發揚的。”

“嗯,這樣就好。” Solmyr 心滿意足的離開了。

“真見鬼!”確認 Solmyr 走遠后,zero 才把在心里憋著的抱怨吐了出來:“好不容易有一段可以休息休息的空檔,這下子又泡湯了!真該死。”正在 zero 忿忿不平的時候,一個幽幽的聲音從旁邊飄了過來:

“zero,剛才你和 Solmyr 講的什么‘概念’、‘禁止拷貝’、‘類層次’…… 這些都是什么呀?還是你給我講講吧 ~~”

zero 轉過頭,看到 pisces 又在以非常“誠懇”的眼神看著他,再想到自己今后的任務,突然間覺得腦袋隱隱的痛了起來 ——— 他似乎有一點明白了,為什么沒事的時候 Solmyr 總在揉自己的太陽穴 ……

posted on 2009-08-19 11:38 李現民 閱讀(432) 評論(0)  編輯 收藏 引用 所屬分類: 絕對盜版

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲美女av在线播放| 亚洲激情在线观看视频免费| 欧美成人影音| 欧美日韩精品一区| 国产精品成人一区二区三区吃奶| 国产精品护士白丝一区av| 亚洲男人的天堂在线| 另类酷文…触手系列精品集v1小说| 欧美/亚洲一区| 中文国产成人精品| 蜜桃久久av| 国产精品一区一区三区| 亚洲欧洲综合另类在线| 午夜精品视频| 夜色激情一区二区| 欧美成人一品| 亚洲欧美一区二区视频| 久久精品亚洲一区| 国产欧美短视频| 亚洲视频一二区| 欧美激情精品久久久久久变态| 亚洲欧美日韩国产中文在线| 欧美日韩不卡| 久久久久九九九九| 亚洲一区二区在| 欧美日本中文| 亚洲裸体在线观看| 久久久久国产精品厨房| 免费亚洲电影在线| 亚洲国产精品久久久久| 久久成人资源| 亚洲一区在线观看免费观看电影高清| 激情欧美日韩一区| 久久精品国产欧美激情| 午夜精品www| 在线视频你懂得一区| 亚洲精品欧美极品| 精品动漫av| 亚洲综合视频网| 韩国精品久久久999| 久久人人精品| 欧美成人国产一区二区| 久久久精品久久久久| 欧美三级欧美一级| 欧美一区二区在线观看| 亚洲日本无吗高清不卡| 亚洲经典三级| 亚洲国产裸拍裸体视频在线观看乱了| 亚洲一区二区精品在线| 亚洲精品影视在线观看| 久久久亚洲国产天美传媒修理工| 国产一区二区三区免费在线观看| 欧美一区午夜精品| 欧美日韩一区二区三区在线观看免| 一片黄亚洲嫩模| 亚洲一区美女视频在线观看免费| 日韩午夜电影av| 亚洲免费婷婷| 亚洲综合首页| 久久国产精品久久国产精品| 亚洲第一伊人| 一区二区三区欧美视频| 国内免费精品永久在线视频| 欧美激情精品久久久久久黑人| 狠狠色狠狠色综合人人| 欧美伊久线香蕉线新在线| 亚洲欧洲在线免费| 免播放器亚洲| 亚洲国产欧美在线人成| 亚洲人成网站999久久久综合 | 亚洲国产99| 噜噜噜噜噜久久久久久91| 免费成人你懂的| 国产精品大全| 亚洲欧美日韩精品久久奇米色影视 | 亚洲无线视频| 亚洲激情在线观看| 欧美电影在线观看完整版| 亚洲激情成人网| 亚洲制服av| 国产一区二区久久| 蜜臀久久久99精品久久久久久| 亚洲国产精品成人综合色在线婷婷| 日韩视频―中文字幕| 欧美日在线观看| 久久国产精品久久久| 欧美大胆a视频| 国产日韩欧美日韩大片| 日韩视频欧美视频| 亚洲区欧美区| 国产精品免费看| 日韩一级片网址| 欧美一区二区三区视频免费播放| 激情成人综合| 欧美日韩一区二区三区视频| 性8sex亚洲区入口| 欧美亚洲免费电影| 国产精品久久九九| 久久在线免费视频| 久久久综合香蕉尹人综合网| 亚洲区一区二| 国产视频一区欧美| 欧美日韩精品一区二区在线播放 | 久久躁日日躁aaaaxxxx| 国产精品网站视频| 亚洲视频在线播放| 欧美粗暴jizz性欧美20| 午夜久久电影网| 99国产精品久久久久久久| 日韩一区二区福利| 黑人巨大精品欧美一区二区| 欧美激情视频网站| 亚洲国产精品999| 欧美在线不卡视频| 一本久久综合亚洲鲁鲁| 韩国三级在线一区| 国产精品久久久久三级| 嫩模写真一区二区三区三州| 午夜精品久久久久久99热| 亚洲国产影院| 欧美成人69| 久久视频在线看| 欧美一区二视频| 亚洲综合日韩在线| 亚洲深夜福利| 99国产精品久久久| 亚洲精品在线免费| 亚洲精品久久久久久久久久久久久 | 狠狠色丁香婷婷综合影院| 国产精品成人一区二区| 欧美看片网站| 欧美黄色大片网站| 中文亚洲视频在线| 日韩午夜av| 夜夜爽av福利精品导航| 亚洲精品影院| 99国产一区| 亚洲一二三级电影| 亚洲一级黄色| 午夜久久美女| 久久国产一区| 美女黄网久久| 欧美黄色一区二区| 欧美日韩另类国产亚洲欧美一级| 欧美激情第8页| 欧美三区不卡| 国产精品亚洲综合色区韩国| 国产精品有限公司| 国内一区二区三区| 亚洲国产精品va在线看黑人| 91久久国产自产拍夜夜嗨| 亚洲精品国产拍免费91在线| 日韩视频免费观看高清完整版| 日韩午夜黄色| 亚洲综合欧美日韩| 久久精品视频导航| 欧美夫妇交换俱乐部在线观看| 欧美黄色日本| 中文日韩在线| 久久久精品国产免费观看同学| 免费高清在线一区| 欧美日韩一区二区免费在线观看| 国产精品美女久久久| 国产综合av| 一本色道久久综合狠狠躁的推荐| 亚洲直播在线一区| 裸体歌舞表演一区二区| 亚洲黄色在线| 午夜精品久久久| 欧美成人中文字幕| 国产伦精品一区二区三区高清 | 欧美激情一区二区三区全黄| 国产精品爱啪在线线免费观看| 免费精品99久久国产综合精品| 欧美激情亚洲视频| 国产日本欧美视频| 亚洲免费观看视频| 欧美中文日韩| 亚洲久久一区二区| 久久国产精品一区二区三区| 欧美精品国产精品| 国内精品视频一区| 亚洲调教视频在线观看| 老司机免费视频一区二区| 99精品免费| 欧美大片免费观看在线观看网站推荐| 久久精品91久久久久久再现| 欧美激情一区二区三级高清视频| 亚洲一区二区网站| 欧美国产亚洲另类动漫| 国产亚洲一区二区三区在线观看 | 黑丝一区二区| 亚洲在线视频网站| 亚洲国产成人精品女人久久久| 久久尤物电影视频在线观看| 99精品国产在热久久婷婷| 久久综合五月| 1000部国产精品成人观看| 欧美一区综合| 亚洲在线中文字幕|