• <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>
            隨筆-341  評(píng)論-2670  文章-0  trackbacks-0
                復(fù)雜的東西寫(xiě)多了,如今寫(xiě)點(diǎn)簡(jiǎn)單的好了。由于功能上的需要,Vczh Library++3.0被我搞得很離譜。為了開(kāi)發(fā)維護(hù)的遍歷、減少粗心犯下的錯(cuò)誤以及增強(qiáng)單元測(cè)試、回歸測(cè)試和測(cè)試工具,因此記錄下一些開(kāi)發(fā)上的小技巧,以便拋磚引玉,造福他人。歡迎高手來(lái)噴,菜鳥(niǎo)膜拜。

                上一篇文章講到了如何檢查內(nèi)存泄露。其實(shí)只要肯用C++的STL里面的高級(jí)功能的話(huà),內(nèi)存泄露是很容易避免的。我在開(kāi)發(fā)Vczh Library++ 3.0的時(shí)候,所有的測(cè)試用例都保證跑完了沒(méi)有內(nèi)存泄露。但是很可惜有些C++團(tuán)隊(duì)不能使用異常,更甚者不允許寫(xiě)構(gòu)造函數(shù)析構(gòu)函數(shù)之類(lèi),前一個(gè)還好,后一個(gè)簡(jiǎn)直就是在用C。當(dāng)然有這些變態(tài)規(guī)定的地方STL都是用不了的,所以我們更加需要扎實(shí)的基礎(chǔ)來(lái)開(kāi)發(fā)C++程序。

                今天這一篇主要還是講指針的問(wèn)題。因?yàn)樯弦黄恼乱还P帶過(guò),今天就來(lái)詳細(xì)講內(nèi)存泄漏或者野指針發(fā)生的各種情況。當(dāng)然我不可能一下子舉出全部的例子,只能說(shuō)一些常見(jiàn)的。

                一、錯(cuò)誤覆蓋內(nèi)存。

                之前提到的不能隨便亂memset其實(shí)就是為了避免這個(gè)問(wèn)題的。其實(shí)memcpy也不能亂用,我們來(lái)看一個(gè)例子,最簡(jiǎn)單的:
             1 #define MAX_STRING 20;
             2 
             3 struct Student
             4 {
             5   char name[MAX_STRING];
             6   char id[MAX_STRING];
             7   int chinese;
             8   int math;
             9   int english;
            10 };

                大家對(duì)這種結(jié)構(gòu)肯定十分熟悉,畢竟是大學(xué)時(shí)候經(jīng)常要寫(xiě)的作業(yè)題……好了,大家很容易看得出來(lái)這其實(shí)是C語(yǔ)言的經(jīng)典寫(xiě)法。我們拿到手之后,一般會(huì)先初始化一下,然后賦值。
            1 Student vczh;
            2 memset(&vczh, 0sizeof(vczh));
            3 strcpy(vczh.name, "vczh");
            4 strcpy(vczh.id, "VCZH'S ID");
            5 vczh.chinese=70;
            6 vczh.math=90;
            7 vczh.english=80;

                為什么要在這里使用memset呢?memset的用處是將一段內(nèi)存的每一個(gè)字節(jié)都設(shè)置成同一個(gè)數(shù)字。這里是0,因此兩個(gè)字符串成員的所有字節(jié)都會(huì)變成0。因此在memset了Student之后,我們通過(guò)正常方法來(lái)訪(fǎng)問(wèn)name和id的時(shí)候都會(huì)得到空串。而且如果Student里面有指針的話(huà),0指針代表的是沒(méi)有指向任何有效對(duì)象,因此這個(gè)時(shí)候?qū)χ羔樦赶虻膶?duì)象進(jìn)行讀寫(xiě)就會(huì)立刻崩潰。對(duì)于其他數(shù)值,0一般作為初始值也不會(huì)有什么問(wèn)題(double什么的要小心)。這就是我們寫(xiě)程序的時(shí)候使用memset的原因。

                好了,如今社會(huì)進(jìn)步,人民當(dāng)家做主了,死程們?cè)僖膊恍枰艿娇蓯旱腃語(yǔ)言剝削了,我們可以使用C++!因此我們借助STL的力量把Student改寫(xiě)成下面這種帶有C++味道的形式:
            1 struct Student
            2 {
            3   std::string name;
            4   std::string id;
            5   int chinese;
            6   int math;
            7   int english;
            8 };

                我們?nèi)匀恍枰獙?duì)Student進(jìn)行初始化,不然三個(gè)分?jǐn)?shù)還是隨機(jī)值。但是我們又不想每一次創(chuàng)建的時(shí)候都對(duì)他們分別進(jìn)行賦值初始化城0。這個(gè)時(shí)候你心里可能還是想著memset,這就錯(cuò)了!在memset的時(shí)候,你會(huì)把std::string內(nèi)部的不知道什么東西也給memset掉。假如一個(gè)空的std::string里面存放的指針指向的是一個(gè)空的字符串而不是用0來(lái)代表空的時(shí)候,一下子內(nèi)部的指針就被你刷成0,等下std::string的析構(gòu)函數(shù)就沒(méi)辦法delete掉指針了,于是內(nèi)存泄露就出現(xiàn)了。有些朋友可能不知道上面那句話(huà)說(shuō)的是什么意思,我們現(xiàn)在來(lái)模擬一下不能memset的std::string要怎么實(shí)現(xiàn)。

                為了讓memset一定出現(xiàn)內(nèi)存泄露,那么std::string里面的指針必須永遠(yuǎn)都指向一個(gè)有效的東西。當(dāng)然我們還需要在字符串進(jìn)行復(fù)制的時(shí)候復(fù)制指針。我們這里不考慮各種優(yōu)化技術(shù),用最簡(jiǎn)單的方法做一個(gè)字符串出來(lái):
             1 class String
             2 {
             3 private:
             4   char* buffer;
             5 
             6 public:
             7   String()
             8   {
             9     buffer=new char[1];
            10     buffer[0]=0;
            11   }
            12 
            13   String(const char* s)
            14   {
            15     buffer=new char[strlen(s)+1];
            16     strcpy(buffer, s);
            17   }
            18 
            19   String(const String& s)
            20   {
            21     buffer=new char[strlen(s.buffer)+1];
            22     strcpy(buffer, s.buffer);
            23   }
            24 
            25   ~String()
            26   {
            27     delete[] buffer;
            28   }
            29 
            30   String& operator=(const String& s)
            31   {
            32     delete[] buffer;
            33     buffer=new char[strlen(s.buffer)+1];
            34     strcpy(buffer, s.buffer);
            35   }
            36 };

                于是我們來(lái)做一下memset。首先定義一個(gè)字符串變量,其次memset掉,讓我們看看會(huì)發(fā)生什么事情:
            1 string s;
            2 memset(&s, 0sizeof(s));

                第一行我們構(gòu)造了一個(gè)字符串s。這個(gè)時(shí)候字符串的構(gòu)造函數(shù)就會(huì)開(kāi)始運(yùn)行,因此strcmp(s.buffer, "")==0。第二行我們把那個(gè)字符串給memset掉了。這個(gè)時(shí)候s.buffer==0。于是函數(shù)結(jié)束了,字符串的析構(gòu)函數(shù)嘗試delete這個(gè)指針。我們知道delete一個(gè)0是不會(huì)有問(wèn)題的,因此程序不會(huì)發(fā)生錯(cuò)誤。我們活生生把構(gòu)造函數(shù)賦值給buffer的new char[1]給丟了!鐵定發(fā)生內(nèi)存泄露!

                好了,提出問(wèn)題總要解決問(wèn)題,我們不使用memset的話(huà),怎么初始化Student呢?這個(gè)十分好做,我們只需要為Student加上構(gòu)造函數(shù)即可:
            1 struct Student
            2 {
            3   .//不重復(fù)那些聲明
            4 
            5   Student():chinese(0),math(0),english(0)
            6   {
            7   }
            8 };

                這樣就容易多了。每當(dāng)我們定義一個(gè)Student變量的時(shí)候,所有的成員都初始化好了。name和id因?yàn)閟tring的構(gòu)造函數(shù)也自己初始化了,因此所有的成員也都初始化了。加入Student用了一半我們想再初始化一下怎么辦呢?也很容易:
            1 Student vczh;
            2 .//各種使用
            3 vczh=Student();

                經(jīng)過(guò)一個(gè)等號(hào)操作符的調(diào)用,舊Student的所有成員就被一個(gè)新的初始化過(guò)的Student給覆蓋了,就如同我們對(duì)一個(gè)int變量重新賦值一樣常見(jiàn)。當(dāng)然因?yàn)楦鞣N復(fù)制經(jīng)常會(huì)出現(xiàn),因此我們也要跟上面貼出來(lái)的string的例子一樣,實(shí)現(xiàn)好那4個(gè)函數(shù)。至此我十分不理解為什么某些團(tuán)隊(duì)不允許使用構(gòu)造函數(shù),我猜就是為了可以memset,其實(shí)是很沒(méi)道理的。

                二、異常。

                咋一看內(nèi)存泄露跟異常好像沒(méi)什么關(guān)系,但實(shí)際上這種情況更容易發(fā)生。我們來(lái)看一個(gè)例子:
             1 char* strA=new char[MAX_PATH];
             2 if(GetXXX(strA, MAX_PATH)==ERROR) goto RELEASE_STRA;
             3 char* strB=new char[MAX_PATH];
             4 if(GetXXX(strB, MAX_PATH)==ERROR) goto RELEASE_STRB;
             5 
             6 DoSomething(strA, strB);
             7 
             8 RELEASE_STRB:
             9 delete[] strB;
            10 RELEASE_STRA:
            11 delete[] strA;

                相信這肯定是大家的常用模式。我在這里也不是教唆大家使用goto,不過(guò)對(duì)于這種例子來(lái)說(shuō),用goto是最優(yōu)美的解決辦法了。但是大家可以看出來(lái),我們用的是C++,因?yàn)檫@里有new。如果DoSomething發(fā)生了異常怎么辦呢?如果GetXXX發(fā)生了異常怎么辦呢?我們這里沒(méi)有任何的try-catch,一有異常,函數(shù)里克結(jié)束,兩行可憐的delete就不會(huì)被執(zhí)行到了,于是內(nèi)存泄漏發(fā)生了

                那我們?nèi)绾伪苊膺@種情況下的內(nèi)存泄露呢?一些可愛(ài)的小盆友可能會(huì)想到,既然是因?yàn)闆](méi)有catch異常才發(fā)生的內(nèi)存泄露,那我們來(lái)catch吧:
             1 char* strA=new char[MAX_PATH];
             2 try
             3 {
             4   if(GetXXX(strA, MAX_PATH)==ERROR) goto RELEASE_STRA;
             5   char* strB=new char[MAX_PATH];
             6   try
             7   {
             8     if(GetXXX(strB, MAX_PATH)==ERROR) goto RELEASE_STRB;
             9     DoSomething(strA, strB);
            10   }
            11   catch()
            12   {
            13     delete[] strB;
            14     throw;
            15   }
            16 }
            17 catch()
            18 {
            19   delete[] strA;
            20   throw;
            21 }
            22 
            23 RELEASE_STRB:
            24 delete[] strB;
            25 RELEASE_STRA:
            26 delete[] strA;

                你能接受嗎?當(dāng)然是不能的。問(wèn)題出在哪里呢?因?yàn)镃++沒(méi)有try-finally。你看這些代碼到處都是雷同的東西,顯然我們需要編譯器幫我們把這些問(wèn)題搞定。最好的解決方法是什么呢?顯然還是構(gòu)造函數(shù)和析構(gòu)函數(shù)。總之記住,如果想要事情成對(duì)發(fā)生,那么使用構(gòu)造函數(shù)和析構(gòu)函數(shù)

                第一步,GetXXX顯然只能支持C模式的東西,因此我們要寫(xiě)一個(gè)支持C++的:
             1 bool GetXXX2(string& s)
             2 {
             3   char* str=new char[MAX_PATH];
             4   bool result;
             5   try
             6   {
             7     result=GetXXX(str, MAX_PATH);
             8     if(result)s=str;
             9   }
            10   catch()
            11   {
            12     delete[] str;
            13     throw;
            14   }
            15   delete[] str;
            16   return result;
            17 }

                借助這個(gè)函數(shù)我們可以看到,因?yàn)橛辛薌etXXX這種C的東西,導(dǎo)致我們多了多少麻煩。不過(guò)這總是一勞永逸的,有了GetXXX2和修改之后的DoSomething2之后,我們就可以用更簡(jiǎn)單的方法來(lái)做了:
            1 string a,b;
            2 if(GetXXX2(a) && GetXXX2(b))
            3 {
            4   DoSomething2(a, b);
            5 }

                多么簡(jiǎn)單易懂。這個(gè)代碼在任何地方發(fā)生了異常,所有new的東西都會(huì)被delete。這就是析構(gòu)函數(shù)的一個(gè)好處。一個(gè)變量的析構(gòu)函數(shù)在這個(gè)變量超出了作用域的時(shí)候一定會(huì)被調(diào)用,無(wú)論代碼是怎么走出去的。

                今天就說(shuō)到這里了。說(shuō)了這么多還是想讓大家不要小看構(gòu)造函數(shù)和析構(gòu)函數(shù)。那種微不足道的因?yàn)橐恍〔糠植皇瞧款i的性能問(wèn)題而放棄構(gòu)造函數(shù)和析構(gòu)函數(shù)的做法,終究是要為了修bug而加班的。只要明白并用好了構(gòu)造函數(shù)、析構(gòu)函數(shù)和異常,那么C++的特性也可以跟C一樣清楚明白便于理解,而且寫(xiě)出來(lái)的代碼更好看的。大家期待第三篇哈。
            posted on 2010-06-23 10:12 陳梓瀚(vczh) 閱讀(11738) 評(píng)論(23)  編輯 收藏 引用 所屬分類(lèi): C++實(shí)用技巧

            評(píng)論:
            # re: C++實(shí)用技巧(二) 2010-06-23 12:53 | OwnWaterloo
            >>0指針代表的是沒(méi)有指向任何有效對(duì)象

            空指針的二進(jìn)制表示并不一定是全0 。
            浮點(diǎn)數(shù)也一樣, 0.0f, 0.0, 0.0lf的二進(jìn)制表示都不一定是全0。
            所以, 即使是C語(yǔ)言, 欲使用memset去將指針初始化為空, 或者將浮點(diǎn)初始化為0, 都是不可移植的。


            >>30 String& operator=(const String& s)
            >>31 {
            >>32 delete[] buffer;
            >>33 buffer=new char[strlen(s.buffer)+1];
            >>34 strcpy(buffer, s.buffer);
            >>35 }

            這個(gè)實(shí)現(xiàn)有問(wèn)題, 當(dāng)出現(xiàn)自賦值的時(shí)候:
            String s;
            s = s;
            this->buffer和s.buffer是同一個(gè)指針。
            32行delete之后, 已經(jīng)是dangling pointer。
            33行傳遞給strlen, 34行傳遞給strcpy都是錯(cuò)誤的。


            要么判斷自賦值的情況:
            if (this!=&s)
            {
            delete[] buffer;
            size_t len = strlen(s.buffer)+1;
            buffer = new char[ len ];
            memcpy(buffer, s.buffer, len );
            }

            但是, 如果new新的buffer時(shí)出現(xiàn)異常, 就會(huì)導(dǎo)致this有一個(gè)dangling pointer。
            為了安全, 可以先new, 再delete:
            size_t len = strlen(s.buffer)+1;
            char* p = new char[len]; // 之后操作都不會(huì)產(chǎn)生異常
            memcpy(p, s.buffer, len );
            delete[] buffer;
            buffer = p;

            先new再delete也可以不用判斷自賦值的情況了。
              回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-23 17:55 | zuhd
            @OwnWaterloo
            哥,你總是這么犀利,我給你留言看到?jīng)]?  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-23 18:33 | OwnWaterloo
            @zuhd
            剛發(fā)現(xiàn)…… 那一天收到的通知太多, 被我直接全部標(biāo)記為已讀, 然后忘了……
            通常用同名gmail郵箱, qq不怎么用……
              回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-23 18:42 | 陳梓瀚(vczh)
            @OwnWaterloo
            為了迅速說(shuō)明問(wèn)題,那個(gè)string其實(shí)還有很多其他的缺陷的……因?yàn)樵谥笾v構(gòu)造函數(shù)和析構(gòu)函數(shù)的一篇上會(huì)著重處理這種case。

            話(huà)說(shuō)回來(lái),我上一篇提到了,這個(gè)系列是默認(rèn)你使用Visual C++的。  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-23 18:46 | 陳梓瀚(vczh)
            @OwnWaterloo
            話(huà)說(shuō)回來(lái),因?yàn)樵贑++里面,字面量“0”的確是代表空指針,因此如果空指針的二進(jìn)制真的不是0的話(huà),我可以認(rèn)為是編譯器的bug。因?yàn)閺恼Z(yǔ)法上講,既然void* p=0;是對(duì)的,那么void* p=(void*)(int)0;也必須是對(duì)的。

            除非從一開(kāi)始就有C++0x的nullptr。  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-23 19:25 | OwnWaterloo
            @陳梓瀚(vczh)
            無(wú)論是C還是C++, 說(shuō)的都是"編譯時(shí)可求出0值的常數(shù)是空指針常量"。
            可以將空指針賦值給任意指針類(lèi)型的變量。
            但并不保證賦值之后指針變量的二進(jìn)制表示也是全0。


            從語(yǔ)法上講:
            void* p = 0;
            void* p=(void*)(int)0;
            都是對(duì)的, 就像:

            float f = 0;
            float f = (float)(int)0;
            而通常f的二進(jìn)制表示并不是全0。
            這是因?yàn)榫幾g器是能看到這里的轉(zhuǎn)型動(dòng)作, 并加入適當(dāng)?shù)牟僮鳌?br>

            但這樣寫(xiě)就是沒(méi)有保證的:
            void* p;
            memset(&p, 0, sizeof p);
            memset函數(shù)中已經(jīng)不知道p的類(lèi)型, 只能將p當(dāng)作byte 數(shù)組。
            就沒(méi)有機(jī)會(huì)為指針類(lèi)型作適當(dāng)?shù)恼{(diào)整。


            再舉個(gè)例子:
            class A {};
            class B {};
            class C : public A, public B {};

            C c;
            C* pc = &c;
            A* pa = pc;
            B* pb = pc;
            assert( pc==pa && pc==pb);

            因?yàn)榫幾g器知道pc,pa,pb的類(lèi)型。
            所以無(wú)論是賦值還是比較, 都會(huì)插入適當(dāng)?shù)牟僮鳌?br>
            而如果使用memset或者memcmp, 就掛了。


            最后, 標(biāo)準(zhǔn)中有句話(huà), 在calloc的腳注里。
            The calloc function allocates space for an array
            of nmemb objects, each of whose size is size.
            The space is initialized to all bits zero.

            Note that this need not be the same as the representation
            of floating-point zero or a null pointer constant.
              回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-23 20:40 | 付翔
            指出個(gè) 小錯(cuò)誤
            #define MAX_STRING 20; 不要分號(hào)   回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-23 21:49 | 陳梓瀚(vczh)
            @OwnWaterloo
            話(huà)說(shuō)回來(lái),那什么地方的空指針的二進(jìn)制不是0?  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-23 21:50 | 陳梓瀚(vczh)
            @付翔
            嗯嗯,這是個(gè)錯(cuò)誤。  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-23 21:52 | 陳梓瀚(vczh)
            @OwnWaterloo
            不過(guò)這個(gè)二進(jìn)制位不是0的事實(shí)更加反映出了構(gòu)造函數(shù)的重要性,memset更加不能隨便來(lái)了(當(dāng)然指的是非Visual C++的情況)  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-23 22:12 | OwnWaterloo
            @陳梓瀚(vczh)
            http://linuxdude.com/Steve_Sumit/C-faq/q5.17.html
            整個(gè)第5章都是講null pointer的。
              回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-23 22:35 | 陳梓瀚(vczh)
            @OwnWaterloo
            都是些從現(xiàn)在開(kāi)始見(jiàn)都沒(méi)見(jiàn)過(guò)的機(jī)器……  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-24 01:18 | 匿名
            delete null
            確定程序不會(huì)拋異常?  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-24 01:46 | 匿名(還是俺)
            char* strA=new char[MAX_PATH];
            這個(gè)也不能保證它一定不會(huì)拋異常。
            new這個(gè)數(shù)組的時(shí)候,加入new到第M個(gè)(M小于MAX_PATH)時(shí)候拋出異常,這時(shí)候也會(huì)有內(nèi)存泄露咯  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-24 03:28 | 陳梓瀚(vczh)
            @匿名(還是俺)
            如果new一個(gè)東西還是敗了,那程序就直接log了崩潰了吧  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-24 10:25 | paul_simon
            1 string s;
            2 memset(&s, 0, sizeof(s));
            //這里有個(gè)問(wèn)題想和博主探討,下文中提到的buffer是String的一個(gè)private成
            //員,memset函數(shù)是不是該在String的成員函數(shù)里面調(diào)用,比如:
            String::mems()
            {
            memset(buffer,0,sizeof(buffer));
            }
            //或者可否考慮在String::String()里加入memset函數(shù)
            //QQ:29975723,謝謝  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-24 21:00 | 陳梓瀚(vczh)
            @paul_simon
            String當(dāng)然要為自己負(fù)責(zé)了,如果String自己覺(jué)得memset合適,那當(dāng)然可以。不過(guò)你那么搞有問(wèn)題,除非buffer是一個(gè)數(shù)組而不是指針。

            但是外部不能memset掉string。  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-25 01:07 | paul_simon
            @陳梓瀚(vczh)
            String的構(gòu)造函數(shù)決定了buffer是個(gè)數(shù)組,這里的String和student是兩個(gè)不同的對(duì)象,String里的buffer成員不能被外部的memset所清零吧?
            所以我說(shuō)的是
            1 string s;
            2 memset(&s, 0, sizeof(s));//這里的實(shí)質(zhì)不是是對(duì)buffer進(jìn)行清零嗎?  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-25 05:43 | 陳梓瀚(vczh)
            @paul_simon
            不是,因?yàn)轭?lèi)的私有部分是不透明的,所以你在外部操作的時(shí)候,不能以知道string的實(shí)現(xiàn)作為前提,人家改了怎么辦,你就要加班了。  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-28 02:11 | 球球
            如果memset用在子類(lèi)的初始化時(shí)...  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-28 03:22 | 陳梓瀚(vczh)
            @球球
            子類(lèi)也不能假設(shè)父類(lèi)是如何實(shí)現(xiàn)的。  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-29 00:06 | chenq
            用那么多代碼去try catch那個(gè)GetXXX,為什么不直接用smart pointer呢  回復(fù)  更多評(píng)論
              
            # re: C++實(shí)用技巧(二) 2010-06-29 09:58 | 陳梓瀚(vczh)
            @chenq
            因?yàn)橛昧藄mart pointer就不能告訴人們C風(fēng)格的丑陋  回復(fù)  更多評(píng)論
              
            精品久久香蕉国产线看观看亚洲| 久久97久久97精品免视看秋霞| 久久久久久久波多野结衣高潮| 久久婷婷色综合一区二区| 久久久久人妻一区二区三区vr| 久久精品二区| 99热成人精品热久久669| 无码人妻少妇久久中文字幕| 日韩乱码人妻无码中文字幕久久| 成人a毛片久久免费播放| 少妇人妻88久久中文字幕| 国产精品成人久久久久久久| 色妞色综合久久夜夜| 久久久久亚洲AV成人网| 久久精品无码一区二区无码| 午夜精品久久久久久| 久久青青草原精品影院| 亚洲精品乱码久久久久久自慰 | 国产69精品久久久久99尤物| 一级A毛片免费观看久久精品| 国产精品美女久久久久av爽| av无码久久久久不卡免费网站 | 国产精品熟女福利久久AV| 久久亚洲中文字幕精品有坂深雪 | 亚洲欧美日韩精品久久| 色综合久久久久综合体桃花网 | 久久人人爽人人爽人人av东京热| 久久精品人妻一区二区三区| 国产精品免费看久久久香蕉| 久久久久久亚洲Av无码精品专口| 久久久久久久波多野结衣高潮 | 亚洲国产成人久久综合一| 1000部精品久久久久久久久| 久久这里只有精品18| 久久99精品久久只有精品 | 蜜臀久久99精品久久久久久小说| 久久久久久久久无码精品亚洲日韩 | 国产精品久久久久久久久免费| 日本人妻丰满熟妇久久久久久| 成人亚洲欧美久久久久| www.久久热|