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

隨筆-341  評論-2670  文章-0  trackbacks-0

    復雜的東西寫多了,如今寫點簡單的好了。由于功能上的需要,Vczh Library++3.0被我搞得很離譜。為了開發維護的遍歷、減少粗心犯下的錯誤以及增強單元測試、回歸測試和測試工具,因此記錄下一些開發上的小技巧,以便拋磚引玉,造福他人。歡迎高手來噴,菜鳥膜拜。

    今天是關于內存的最后一篇了。上一篇文章講了為什么不能對一個東西隨便memset。里面的demo代碼出了點小bug,不過我不喜歡在發文章的時候里面的demo代碼也拿去編譯和運行,所以大家有什么發現的問題就評論吧。這樣也便于后來的人不會受到誤導。這次說的仍然是構造函數和析構函數的事情,不過我們將通過親手開發一個智能指針的方法,知道引用計數如何幫助管理資源,以及錯誤使用引用計數的情況。

    首先先來看一下智能指針是如何幫助我們管理內存的?,F在智能指針的實現非常多,我就假設這個類型叫Ptr<T>吧。這跟Vczh Library++ 3.0所使用的實現一樣。

 1 class Base
 2 {
 3 public:
 4   virtual ~Base(){}
 5 };
 6 
 7 class Derived1 : public Base
 8 {
 9 };
10 
11 class Derived2 : public Base
12 {
13 };
14 
15 //---------------------------------------
16 
17 List<Ptr<Base>> objects;
18 objects.Add(new Derived1);
19 objects.Add(new Derived2);
20 
21 List<Ptr<Base>> objects2;
22 objects2.Add(objects[0]);

    當然這里的List也是Vczh Library++3.0實現的,不過這玩意兒跟vector也好跟C#的List也好都是一個概念,因此也就不需要多加解釋了。我們可以看到智能指針的一個好處,只要沒有循環引用出現,你無論怎么復制它,最終總是可以被析構掉的。另一個例子告訴我們智能指針如何處理類型轉換:
1 Ptr<Derived1> d1=new Derived1;
2 Ptr<Base> b=d1;
3 Ptr<Derived2> d2=b.Cast<Derived2>();
4 // d2是空,因為b指向的是Derived1而不是Derived2。

    這就如同我們Derived1*可以隱式轉換到Base*,而當你使用dynamic_cast<Derived2*>(static_cast<Base*>(new Derived1))會得到0一樣。智能指針在幫助我們析構對象的同時,也要做好類型轉換的工作。

    好了,現在先讓我們一步一步做出那個Ptr<T>。我們需要清楚這個智能指針所要實現的功能是什么,然后我們一個一個來做。首先讓我們列出一張表:
    1、沒有參數構造的時候,初始化為空
    2、使用指針構造的時候,擁有那個指針,并且在沒有任何智能指針指向那個指針的時候刪除掉該指針。
    3、智能指針進行復制的時候,兩個智能指針共同擁有該內部指針。
    4、智能指針可以使用新的智能指針或裸指針重新賦值。
    5、需要支持隱式指針類型轉換,static_cast不支持而dynamic_cast支持的轉換則使用Cast<T2>()成員函數來解決。
    6、如果一個裸指針直接用來創建兩個智能指針的話,期望的情況是當兩個智能指針析構掉的時候,該指針會被delete兩次從而崩潰。
    7、不處理循環引用。

    最后兩點實際上是錯誤使用智能指針的最常見的兩種情況。我們從1到5一個一個實現。首先是1。智能指針可以隱式轉換成bool,可以通過operator->()拿到內部的T*。在沒有使用參數構造的時候,需要轉換成false,以及拿到0:
 1 template<typename T>
 2 class Ptr
 3 {
 4 private:
 5   T* pointer;
 6   int* counter;
 7 
 8   void Increase()
 9   {
10     if(counter)++*counter;
11   }
12 
13   void Decrease()
14   {
15     if(counter && --*counter==0)
16     {
17       delete counter;
18       delete pointer;
19       counter=0;
20       pointer=0;
21     }
22   }
23 
24 public:
25   Ptr():pointer(0),counter(0)
26   {
27   }
28 
29   ~Ptr()
30   {
31     Decrease();
32   }
33 
34   operator bool()const
35   {
36     return counter!=0;
37   }
38 
39   T* operator->()const
40   {
41     return pointer;
42   }
43 };

    在這里我們實現了構造函數和析構函數。構造函數把內部指針和引用計數的指針都初始化為空,而析構函數則進行引用計數的減一操作。另外兩個操作符重載很容易理解。我們主要來看看Increase函數和Decrease函數都分別做了什么。Increase函數在引用計數存在的情況下,把引用計數加一。而Decrease函數在引用計數存在的情況下,把引用計數減一,如果引用計數在減一過程中變成了0,則刪掉擁有的資源。

    當然到了這個時候智能指針還不能用,我們必須替他加上復制構造函數,operator=操作符重載以及使用指針賦值的情況。首先讓我們來看使用指針賦值的話我們應該加上什么:
 1   Ptr(T* p):pointer(0),counter(0)
 2   {
 3     *this=p;
 4   }
 5 
 6   Ptr<T>& operator=(T* p)
 7   {
 8     Decrease();
 9     if(p)
10     {
11       pointer=p;
12       counter=new int(1);
13     }
14     else
15     {
16       pointer=0;
17       counter=0;
18     }
19     return *this;
20   }

    這里還是偷工減料了的,構造函數接受了指針的話,還是轉給operator=去調用了。當一個智能指針被一個新指針賦值的時候,我們首先要減掉一個引用計數,因為原來的指針再也不被這個智能指針共享了。之后就進行判斷,如果來的是0,那么就變成空。如果不是0,就擁有該指針,引用計數初始化成1。于是我們就可以這么使用了:
1 Ptr<Base> b=new Derived1;
2 Ptr<Derived2> d2=new Derived2;

    讓我們開始復制他們吧。復制的要領是,先把之前擁有的指針脫離掉,然后連接到一個新的智能指針上面去。我們知道非空智能指針有多少個,總的引用計數的和就是多少,只是分配到各個指針上面的數字不一樣而已:
 1   Ptr(const Ptr<T>& p):pointer(p.pointer),counter(p.counter)
 2   {
 3     Increase();
 4   }
 5 
 6   Ptr<T>& operator=(const Ptr<T>& p)
 7   {
 8     if(this!=&p)
 9     {
10       Decrease();
11       pointer=p.pointer;
12       counter=p.counter;
13       Increase();
14     }
15     return *this;
16   }

    在上一篇文章有朋友指出重載operator=的時候需要考慮是不是自己賦值給自己,其實這是很正確的。我們寫每一類的時候,特別是當類擁有自己控制的資源的時候,需要非常注意這件事情。當然如果只是復制幾個對象而不會new啊delete還是close什么handle,那檢查不檢查也無所謂了。在這里我們非常清楚,當增加一個新的非空智能指針的時候,引用計數的總和會加一。當修改一個非空智能指針的結果也是非空的時候,引用計數的和保持不變。當然這是應該的,因為我們需要在所有非空智能指針都被毀掉的時候,釋放受保護的所有資源。

    到了這里一個智能指針基本上已經能用了,但是還不能處理父類子類的情況。這個是比較麻煩的,一個Ptr<Derived>事實上沒有權限訪問Ptr<Base>的內部對象。因此我們需要通過友元類來解決這個問題。現在讓我們來添加兩個新的函數吧,從一個任意的Ptr<C>復制到Ptr<T>,然后保證只有當C*可以隱式轉換成T*的時候編譯能夠通過:
 1   template<X> friend class Ptr;
 2 
 3   template<typename C>
 4   Ptr(const Ptr<C>& p):pointer(p.pointer),counter(p.counter)
 5   {
 6     Increase();
 7   }
 8 
 9   template<typename C>
10   Ptr<T>& operator=(const Ptr<C>& p)
11   {
12     Decrease();
13     pointer=p.pointer;
14     counter=p.counter;
15     Increase();
16     return *this;
17   }

    注意這里我們的operator=并不用檢查是不是自己給自己賦值,因為這是兩個不同的類,相同的話會調用上面那個operator=的。如果C*不能隱式轉換到T*的話,這里的pointer=p.pointer就會失敗,從而滿足了我們的要求。

    現在我們能夠做的事情就更多了:
1 Ptr<Derived1> d1=new Derived1;
2 Ptr<Base> b=d1;

    于是我們只剩下最后一個Cast函數了。這個函數內部使用dynamic_cast來做判斷,如果轉換失敗,會返回空指針:
 1   tempalte<typename C>
 2   Ptr<C> Cast()const
 3   {
 4     C* converted=dynamic_cast<C*>(pointer);
 5     Ptr<C> result;
 6     if(converted)
 7     {
 8       result.pointer=converted;
 9       result.counter=counter;
10       Increase();
11     }
12     return result;
13   }

    這是一種hack的方法,平時是不鼓勵的……不過因為操作的都是Ptr,而且特化Ptr也是使用錯誤的一種,所以這里就不管了。我們會檢查dynamic_cast的結果,如果成功了,那么會返回一個非空的新智能指針,而且這個時候我們也要記住Increase一下。

    好了,基本功能就完成了。當然一個智能指針還要很多其他功能,譬如說比較什么的,這個就你們自己搞定哈。

    指針和內存就說到這里了,下一篇講如何利用一個好的IDE構造輕量級單元測試系統。我們都說好的工具能夠提高生產力,因此這種方法不能脫離一個好的IDE使用。
posted on 2010-06-23 23:03 陳梓瀚(vczh) 閱讀(9876) 評論(15)  編輯 收藏 引用 所屬分類: C++實用技巧

評論:
# re: C++實用技巧(三) 2010-06-24 23:22 | zuhd
全文可以理解為一段共享內存的故事  回復  更多評論
  
# re: C++實用技巧(三) 2010-06-25 05:31 | SonicLing
效率存在問題。
12 counter=new int(1);
感覺這個new完全沒必要啊。

而且Ptr<T>的值傳遞會很慢,完全沒有了指針的速度啊。  回復  更多評論
  
# re: C++實用技巧(三) 2010-06-25 05:41 | 陳梓瀚(vczh)
@SonicLing
我不會為了不是瓶頸部分的效率而讓開發效率大大下降的。加班的原因還不是因為程序寫得慢。  回復  更多評論
  
# re: C++實用技巧(三) 2010-06-25 05:42 | 陳梓瀚(vczh)
@SonicLing
這個new有必要,counter==1也是要有counter的。  回復  更多評論
  
# re: C++實用技巧(三) 2010-06-25 06:17 | SonicLing
@陳梓瀚(vczh)
我的意思是直接用int counter,而不是指針。  回復  更多評論
  
# re: C++實用技巧(三) 2010-06-25 17:48 | zuhd
@SonicLing


全文可以理解為一段共享內存的故事

你顯然沒有理解這段話的意思  回復  更多評論
  
# re: C++實用技巧(三) 2010-06-25 20:02 | 陳梓瀚(vczh)
@SonicLing
那一共有三個Ptr,其中一個死了,你不可能同時改了另外兩個的counter。謝謝。  回復  更多評論
  
# re: C++實用技巧(三) 2010-06-26 07:58 | SonicLing
@陳梓瀚(vczh)
恩,我理解錯了。我寫過類似的指針,只不過把counter放在對象里,共享對象同時也共享了counter,這樣在傳遞和內存分配方面效率高一點,成員只包含一個指針,優化收益也高一些,免去了過多的new/delete。因為跟你一樣,也在寫編譯/解釋器,所以非常看重效率。  回復  更多評論
  
# re: C++實用技巧(三) 2010-06-26 08:10 | 陳梓瀚(vczh)
@SonicLing
T顯然是任意的,你不能修改。所以這才是我分出來的原因。  回復  更多評論
  
# re: C++實用技巧(三) 2010-06-26 08:10 | 陳梓瀚(vczh)
@SonicLing
虛擬機效率高就好了,編譯器沒必要。  回復  更多評論
  
# re: C++實用技巧(三) 2010-06-26 17:52 | 飯中淹
為什么不把pointer和counter放在一個結構里,new這個結構?
我做這個東西的時候,就是做這樣一個core_struct,然后加一個core_struct池來維護內存。所有的Ptr傳遞和保存的就是這個core_struct的指針。  回復  更多評論
  
# re: C++實用技巧(三) 2010-06-26 21:02 | 陳梓瀚(vczh)
@飯中淹
你這種做法在遇到類型轉換的時候就有問題了。  回復  更多評論
  
# re: C++實用技巧(三) 2010-07-01 00:32 |
使用type_traits模板技術可以非常漂亮的解決智能指針的子父類轉換問題,如果要安全轉換子父類指針,根本辦法是打開RTTI,在智能指針實現的內部使用dynamic_cast,并在出錯時拋出異常。
但是RTTI我只建議在個別模塊中使用,這樣不會影響別的模塊的對象內存結構,這種結構會占用多余的內存。  回復  更多評論
  
# re: C++實用技巧(三) 2010-07-01 01:06 | 陳梓瀚(vczh)
@釀
我這不就這么做嗎,請認真閱讀。話說回來,RTTI在不是瓶頸的時候,我絕對不關掉它。  回復  更多評論
  
# re: C++實用技巧(三) 2014-10-30 00:15 | 大花貓
Ptr<T>& operator=(T* p)這個函數怎么沒有檢查自賦值  回復  更多評論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            激情综合电影网| 亚洲日本久久| 国内精品久久久久影院 日本资源| 欧美国产日韩精品免费观看| 免费在线国产精品| 欧美freesex8一10精品| 免费亚洲电影在线观看| 玖玖在线精品| 欧美极品一区二区三区| 欧美视频在线观看免费| 亚洲三级电影在线观看| 91久久久亚洲精品| 亚洲精品国产精品乱码不99| 中文无字幕一区二区三区| 亚洲综合不卡| 六月丁香综合| 国产精品日韩电影| 亚洲国内自拍| 亚洲欧美日韩一区二区| 久久乐国产精品| 91久久精品美女高潮| 亚洲欧美日本国产有色| 久久综合久久久久88| 欧美日韩另类丝袜其他| 国产无一区二区| 亚洲精品日韩激情在线电影 | 麻豆成人综合网| 欧美网站大全在线观看| 国产有码一区二区| 亚洲精品系列| 久久久久久久一区二区| 亚洲另类春色国产| 久久久久久午夜| 国产精品国产三级国产aⅴ无密码 国产精品国产三级国产aⅴ入口 | 亚洲乱码国产乱码精品精可以看| 亚洲欧美日韩精品久久久久| 欧美jjzz| 在线日本成人| 久久国产精品电影| 夜夜嗨av一区二区三区| 裸体一区二区| 经典三级久久| 久久精品观看| 亚洲在线一区二区| 欧美日韩1区2区| 亚洲人成毛片在线播放| 久久蜜桃精品| 久久精品青青大伊人av| 国产精品午夜在线观看| 亚洲私人影院在线观看| 亚洲欧洲一区二区天堂久久| 老司机精品久久| 在线精品视频一区二区| 久久久久久久一区二区| 欧美一区二区三区四区高清| 国产精品久久9| 亚洲一区二三| 亚洲一区二区成人在线观看| 国产精品成人午夜| 午夜欧美精品久久久久久久| 亚洲午夜电影在线观看| 国产精品二区二区三区| 亚洲女同在线| 亚洲欧美日韩在线不卡| 国产精品99久久久久久久vr| 欧美乱人伦中文字幕在线| 亚洲国产精品久久久久婷婷老年| 久久综合色婷婷| 久久国产精品网站| 一区二区三区在线看| 美女主播视频一区| 美腿丝袜亚洲色图| 亚洲免费福利视频| 亚洲裸体在线观看| 国产精品日韩一区| 久久久精品网| 蜜臀久久久99精品久久久久久| 亚洲黄色一区| 一本到高清视频免费精品| 国产精品视频yy9099| 久久精品国产一区二区三区| 久久久久久久97| 99亚洲一区二区| 亚洲欧美日韩系列| 亚洲区国产区| 亚洲一二三四久久| 精品粉嫩aⅴ一区二区三区四区| 欧美成人福利视频| 欧美图区在线视频| 久久综合中文色婷婷| 欧美激情一区二区三区在线视频观看 | 免费日韩成人| 亚洲自拍偷拍色片视频| 欧美一区二区在线| 亚洲免费高清视频| 久久av一区二区三区漫画| 亚洲欧洲日产国产综合网| 一区二区三区黄色| 一区二区三区在线高清| 亚洲天堂久久| 亚洲精品一区二区三| 午夜精品久久久久久99热软件| 亚洲电影免费在线观看| 亚洲综合色丁香婷婷六月图片| 亚洲福利视频免费观看| 亚洲一区国产视频| 99视频精品| 美脚丝袜一区二区三区在线观看 | 亚洲激情小视频| 国产欧美一区二区三区在线看蜜臀 | 一二三区精品| 最新亚洲一区| 久久精品视频亚洲| 先锋资源久久| 欧美日韩在线另类| 亚洲韩日在线| 亚洲大片在线| 久久激情五月婷婷| 久久成人人人人精品欧| 国产精品黄色在线观看| 夜夜嗨一区二区| 欧美区一区二区三区| 久久综合导航| 国产一区二区激情| 亚洲欧美三级伦理| 亚洲免费在线电影| 欧美涩涩视频| 9色精品在线| 亚洲一区二区三区精品视频| 欧美精品亚洲二区| 亚洲人成网站影音先锋播放| 在线精品国产成人综合| 久久人人爽人人爽爽久久| 久久九九全国免费精品观看| 国产精品综合不卡av| 亚洲午夜一二三区视频| 亚洲欧美日韩一区二区在线| 国产精品高潮粉嫩av| 中文久久精品| 欧美一区二区三区成人| 国产偷久久久精品专区| 欧美一级片一区| 久久蜜桃香蕉精品一区二区三区| 国内精品视频在线播放| 久久免费偷拍视频| 美女尤物久久精品| 亚洲精品色婷婷福利天堂| 欧美高清在线播放| 亚洲久久在线| 性久久久久久久| 国产一区二区0| 久久久国产午夜精品| 欧美韩日一区| 宅男66日本亚洲欧美视频 | 亚洲电影在线看| 蜜臀久久99精品久久久久久9| 亚洲国产成人在线播放| 这里只有精品电影| 国产精品日韩在线播放| 久久精品亚洲精品| 亚洲国产精品久久久久秋霞蜜臀| 99在线|亚洲一区二区| 欧美午夜视频一区二区| 欧美在线一二三| 亚洲欧洲中文日韩久久av乱码| 亚洲影院色无极综合| 一区二区在线免费观看| 欧美日韩亚洲系列| 久久精品国产第一区二区三区最新章节 | 国产一区二区三区在线观看精品| 久久美女艺术照精彩视频福利播放| 欧美国产乱视频| 香蕉亚洲视频| 亚洲日本在线视频观看| 国产嫩草一区二区三区在线观看| 久久久国产一区二区| 一本久道久久综合狠狠爱| 久久蜜臀精品av| 亚洲一级在线观看| 亚洲电影欧美电影有声小说| 国产精品免费网站| 欧美国产日韩一二三区| 欧美一区二区三区四区在线观看地址| 欧美激情免费在线| 久久精品国产精品亚洲综合| 亚洲私人影院在线观看| 亚洲精品乱码久久久久久蜜桃91| 亚洲一区二区av电影| 亚洲国产精品www| 久久琪琪电影院| 午夜欧美精品| 亚洲影视综合| 这里是久久伊人| 亚洲精品一区二区三区樱花 | 午夜精品久久久久久| 99在线精品视频| 亚洲国产日韩欧美在线图片| 久久亚洲精品一区| 久久精品av麻豆的观看方式| 午夜精品久久久久久久99热浪潮|