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

longshanks

  C++博客 :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
  14 Posts :: 0 Stories :: 214 Comments :: 0 Trackbacks

常用鏈接

留言簿(10)

我參與的團(tuán)隊(duì)

搜索

  •  

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

C++的營(yíng)養(yǎng)

莫華楓
    動(dòng)物都會(huì)攝取食物,吸收其中的營(yíng)養(yǎng),用于自身生長(zhǎng)和活動(dòng)。然而,并非食物中所有的物質(zhì)都能為動(dòng)物所吸收。那些無(wú)法消化的物質(zhì),通過(guò)消化道的另一頭(某些動(dòng) 物消化道只有一頭)排出體外。不過(guò),一種動(dòng)物無(wú)法消化的排泄物,是另一種動(dòng)物(生物)的食物,后者可以從中攝取所需的營(yíng)養(yǎng)。
    一門編程語(yǔ)言,對(duì)于程序員而言,如同食物那樣,包含著所需的養(yǎng)分。當(dāng)然也包含著無(wú)法消化的東西。不同的是,隨著程序員不斷成長(zhǎng),會(huì)逐步消化過(guò)去無(wú)法消化的那些東西。
    C++可以看作一種成分復(fù)雜的食物,對(duì)于多數(shù)程序員而言,是無(wú)法完全消化的。正因?yàn)槿绱?,很多程序員認(rèn)為C++太難以消化,不應(yīng)該去吃它。但是,C++的 營(yíng)養(yǎng)不可謂不豐富,就此舍棄,而不加利用,則是莫大的罪過(guò)。好在食物可以通過(guò)加工,變得易于吸收,比如說(shuō)發(fā)酵。鑒于程序員們的消化能力的差異,也為了讓C ++的營(yíng)養(yǎng)能夠造福他人,我就暫且扮演一回酵母菌,把C++的某些營(yíng)養(yǎng)單獨(dú)提取出來(lái),并加以分解,讓那些消化能力不太強(qiáng)的程序員也能享受它的美味。:)
    (為了讓這些營(yíng)養(yǎng)便于消化,我將會(huì)用C#做一些案例。選擇C#的原因很簡(jiǎn)單,因?yàn)槲沂煜ぁ?))

RAII

    RAII,好古怪的營(yíng)養(yǎng)?。∷娜Q應(yīng)該是“Resource Acquire Is Initial”。這是C++創(chuàng)始人Bjarne Stroustrup發(fā)明的詞匯,比較令人費(fèi)解。說(shuō)起來(lái),RAII的含義倒也不算復(fù)雜。用白話說(shuō)就是:在類的構(gòu)造函數(shù)中分配資源,在析構(gòu)函數(shù)中釋放資源。 這樣,當(dāng)一個(gè)對(duì)象創(chuàng)建的時(shí)候,構(gòu)造函數(shù)會(huì)自動(dòng)地被調(diào)用;而當(dāng)這個(gè)對(duì)象被釋放的時(shí)候,析構(gòu)函數(shù)也會(huì)被自動(dòng)調(diào)用。于是乎,一個(gè)對(duì)象的生命期結(jié)束后將會(huì)不再占用 資源,資源的使用是安全可靠的。
    下面便是在C++中實(shí)現(xiàn)RAII的典型代碼:
        class file
        {
        
public:
            file(
string const& name) {
                   m_fileHandle
=open_file(name.cstr());
            }
            
~file() {
                   close_file(m_fileHandle);
            }
            ...
        
private:
            handle m_fileHandle;
        }
    很典型的“在構(gòu)造函數(shù)里獲取,在析構(gòu)函數(shù)里釋放”。如果我寫下代碼:       
        void fun1() {
            file myfile(
"my.txt");
            ... 
//操作文件
        }
    //此處銷毀對(duì)象,調(diào)用析構(gòu)函數(shù),釋放資源
    當(dāng)函數(shù)結(jié)束時(shí),局部對(duì)象myfile的生命周期也結(jié)束了,析構(gòu)函數(shù)便會(huì)被調(diào)用,資源會(huì)得到釋放。而且,如果函數(shù)中的代碼拋出異常,那么析構(gòu)函數(shù)也會(huì)被調(diào)用,資源同樣會(huì)得到釋放。所以,在RAII下,不僅僅資源安全,也是異常安全的。
    但是,在如下的代碼中,資源不是安全的,盡管我們實(shí)現(xiàn)了RAII:
         void fun2() {
             file pfile
=new file("my.txt");
                ... 
//操作文件
         }
    因?yàn)槲覀冊(cè)诙焉蟿?chuàng)建了一個(gè)對(duì)象(通過(guò)new),但是卻沒(méi)有釋放它。我們必須運(yùn)用delete操作符顯式地加以釋放:
        void fun3() {
             file pfile
=new file("my.txt");
                ... 
//操作文件
                delete pfile;
        }
    否則,非但對(duì)象中的資源得不到釋放,連對(duì)象本身的內(nèi)存也得不到回收。(將來(lái),C++的標(biāo)準(zhǔn)中將會(huì)引入GC(垃圾收集),但正如下面分析的那樣,GC依然無(wú)法確保資源的安全)。
    現(xiàn)在,在fun3(),資源是安全的,但卻不是異常安全的。因?yàn)橐坏┖瘮?shù)中拋出異常,那么delete pfile;這句代碼將沒(méi)有機(jī)會(huì)被執(zhí)行。C++領(lǐng)域的諸位大牛們告誡我們:如果想要在沒(méi)有GC的情況下確保資源安全和異常安全,那么請(qǐng)使用智能指針:
        void fun4() {
              shared_ptr
<file> spfile(new file("my.txt"));
              ... 
//操作文件
        }
  //此處,spfile結(jié)束生命周期的時(shí)候,會(huì)釋放(delete)對(duì)象
    那么,智能指針又是怎么做到的呢?下面的代碼告訴你其中的把戲(關(guān)于智能指針的更進(jìn)一步的內(nèi)容,請(qǐng)參考std::auto_ptr,boost或tr1的智能指針):
        template<typename T>
        
class smart_ptr
        
{
        
public:
            smart_ptr(T
* p):m_ptr(p) {}
            
~smart_ptr() { delete m_ptr; }
            ...
        
private:
            T
* m_ptr;
        }
    沒(méi)錯(cuò),還是RAII。也就是說(shuō),智能指針通過(guò)RAII來(lái)確保內(nèi)存資源的安全,也間接地使得對(duì)象上的RAII得到實(shí)施。不過(guò),這里的RAII并不是十分嚴(yán) 格:對(duì)象(所占的內(nèi)存也是資源)的創(chuàng)建(資源獲取)是在構(gòu)造函數(shù)之外進(jìn)行的。廣義上,我們也把它劃歸RAII范疇。但是,Matthew Wilson在《Imperfect C++》一書中,將其獨(dú)立出來(lái),稱其為RRID(Resource Release Is Destruction)。RRID的實(shí)施需要在類的開(kāi)發(fā)者和使用者之間建立契約,采用相同的方法獲取和釋放資源。比如,如果在shared_ptr構(gòu)造 時(shí)使用malloc(),便會(huì)出現(xiàn)問(wèn)題,因?yàn)閟hared_ptr是通過(guò)delete釋放對(duì)象的。
    對(duì)于內(nèi)置了GC的語(yǔ)言,資源管理相對(duì)簡(jiǎn)單。不過(guò),事情并非總是這樣。下面的C#代碼摘自MSDN Library的C#編程指南,我略微改造了一下:
        static void CodeWithoutCleanup()
        
{
            System.IO.FileStream file 
= null;
            System.IO.FileInfo fileInfo 
= new System.IO.FileInfo("C:\file.txt");
            file 
= fileInfo.OpenWrite();
            file.WriteByte(
0xF);
        }
    那么資源會(huì)不會(huì)泄漏呢?這取決于對(duì)象的實(shí)現(xiàn)。如果通過(guò)OpenWrite()獲得的FileStream對(duì)象,在析構(gòu)函數(shù)中執(zhí)行了文件的釋放操作,那么資 源最終不會(huì)泄露。因?yàn)镚C最終在執(zhí)行GC操作的時(shí)候,會(huì)調(diào)用Finalize()函數(shù)(C#類的析構(gòu)函數(shù)會(huì)隱式地轉(zhuǎn)換成Finalize()函數(shù)的重 載)。這是由于C#使用了引用語(yǔ)義(嚴(yán)格地講,是對(duì)引用類型使用引用語(yǔ)義),一個(gè)對(duì)象實(shí)際上不是對(duì)象本身,而是對(duì)象的引用。如同C++中的那樣,引用在離 開(kāi)作用域時(shí),是不會(huì)釋放對(duì)象的。否則,便無(wú)法將一個(gè)對(duì)象直接傳遞到函數(shù)之外。在這種情況下,如果沒(méi)有顯式地調(diào)用Close()之類的操作,資源將不會(huì)得到 立刻釋放。但是像文件、鎖、數(shù)據(jù)庫(kù)鏈接之類屬于重要或稀缺的資源,如果等到GC執(zhí)行回收,會(huì)造成資源不足。更有甚者,會(huì)造成代碼執(zhí)行上的問(wèn)題。我曾經(jīng)遇到 過(guò)這樣一件事:我執(zhí)行了一個(gè)sql操作,獲得一個(gè)結(jié)果集,然后執(zhí)行下一個(gè)sql,結(jié)果無(wú)法執(zhí)行。這是因?yàn)槲沂褂玫腟QL Server 2000不允許在一個(gè)數(shù)據(jù)連接上同時(shí)打開(kāi)兩個(gè)結(jié)果集(很多數(shù)據(jù)庫(kù)引擎都是這樣)。第一個(gè)結(jié)果集用完后沒(méi)有立刻釋放,而GC操作則尚未啟動(dòng),于是便造成在一 個(gè)未關(guān)閉結(jié)果集的數(shù)據(jù)連接上無(wú)法執(zhí)行新的sql的問(wèn)題。
    所以,只要涉及了內(nèi)存以外的資源,應(yīng)當(dāng)盡快釋放。(當(dāng)然,如果內(nèi)存能夠盡快釋放,就更好了)。對(duì)于上述CodeWithoutCleanup()函數(shù),應(yīng)當(dāng)在最后調(diào)用file對(duì)象上的Close()函數(shù),以便釋放文件:
        static void CodeWithoutCleanup()
        
{
            System.IO.FileStream file 
= null;
            System.IO.FileInfo fileInfo 
= new System.IO.FileInfo("C:\file.txt");
            file 
= fileInfo.OpenWrite();
            file.WriteByte(
0xF);
            file.Close();
        }
    現(xiàn)在,這個(gè)函數(shù)是嚴(yán)格資源安全的,但卻不是嚴(yán)格異常安全的。如果在文件的操作中拋出異常,Close()成員將得不到調(diào)用。此時(shí),文件也將無(wú)法及時(shí)關(guān)閉,直到GC完成。為此,需要對(duì)異常作出處理:
        static void CodeWithCleanup()
        
{
            System.IO.FileStream file 
= null;
            System.IO.FileInfo fileInfo 
= null;
            
try
            
{
                fileInfo 
= new System.IO.FileInfo("C:\file.txt");
                file 
= fileInfo.OpenWrite();
                file.WriteByte(
0xF);
            }

            
catch(System.Exception e)
            
{
                System.Console.WriteLine(e.Message);
            }

            
finally
            
{
                
if (file != null)
                
{
                    file.Close();
                }

            }

       }
    try-catch-finally是處理這種情況的標(biāo)準(zhǔn)語(yǔ)句。但是,相比前面的C++代碼fun1()和fun4()繁瑣很多。這都是沒(méi)有RAII的后果啊。下面,我們就來(lái)看看,如何在C#整出RAII來(lái)。
    一個(gè)有效的RAII應(yīng)當(dāng)包含兩個(gè)部分:構(gòu)造/析構(gòu)函數(shù)的資源獲取/釋放和確定性的析構(gòu)函數(shù)調(diào)用。前者在C#中不成問(wèn)題,C#有構(gòu)造函數(shù)和析構(gòu)函數(shù)。不過(guò), C#的構(gòu)造函數(shù)和析構(gòu)函數(shù)是不能用于RAII的,原因一會(huì)兒會(huì)看到。正確的做法是讓一個(gè)類實(shí)現(xiàn)IDisposable接口,在IDisposable:: Dispose()函數(shù)中釋放資源:
        class RAIIFile : IDisposable
        
{
        
public RAIIFile(string fn) {
                System.IO.FileInfo fileInfo 
= new System.IO.FileInfo(fn);
                file 
= fileInfo.OpenWrite();
            }


        
public void Dispose() {
                  file.Close();
              }


        
private System.IO.FileStream file = null;
        }
    下一步,需要確保文件在退出作用域,或發(fā)生異常時(shí)被確定性地釋放。這項(xiàng)工作需要通過(guò)C#的using語(yǔ)句實(shí)現(xiàn):
        static void CodeWithRAII()
        
{
            
using(RAIIFile file=new RAIIFile("C:\file.txt"))
            
{
                ... 
//操作文件
            }
 //文件釋放
        }
    一旦離開(kāi)using的作用域,file.Dispose()將被調(diào)用,文件便會(huì)得到釋放,即便拋出異常,亦是如此。相比CodeWithCleanup ()中那坨雜亂繁復(fù)的代碼,CodeWithRAII()簡(jiǎn)直可以算作賞心悅目。更重要的是,代碼的簡(jiǎn)潔和規(guī)則將會(huì)大幅減少出錯(cuò)可能性。值得注意的是 using語(yǔ)句只能作用于實(shí)現(xiàn)IDisposable接口的類,即便實(shí)現(xiàn)了析構(gòu)函數(shù)也不行。所以對(duì)于需要得到RAII的類,必須實(shí)現(xiàn) IDisposable。通常,凡是涉及到資源的類,都應(yīng)該實(shí)現(xiàn)這個(gè)接口,便于日后使用。實(shí)際上,.net庫(kù)中的很多與非內(nèi)存資源有關(guān)的類,都實(shí)現(xiàn)了 IDisposable,都可以利用using直接實(shí)現(xiàn)RAII。
    但是,還有一個(gè)問(wèn)題是using無(wú)法解決的,就是如何維持類的成員函數(shù)的RAII。我們希望一個(gè)類的成員對(duì)象在該類實(shí)例創(chuàng)建的時(shí)候獲取資源,而在其銷毀的時(shí)候釋放資源:
        class X
        
{
        
public:
            X():m_file(
"c:\file.txt"{}
        
private:
            File m_file;    
//在X的實(shí)例析構(gòu)時(shí)調(diào)用File::~File(),釋放資源。
        }
    但是在C#中無(wú)法實(shí)現(xiàn)。由于uing中實(shí)例化的對(duì)象在離開(kāi)using域的時(shí)候便釋放了,無(wú)法在構(gòu)造函數(shù)中使用:
        class X
        
{
            
public X() {
                
using(m_file=new RAIIFile("C:\file.txt"))
                
{
                }
//此處m_file便釋放了,此后m_file便指向無(wú)效資源
            }

            pravite RAIIFile m_file;
        }
    對(duì)于成員對(duì)象的RAII只能通過(guò)在析構(gòu)函數(shù)或Dispose()中手工地釋放。我還沒(méi)有想出更好的辦法來(lái)。
    至此,RAII的來(lái)龍去脈已經(jīng)說(shuō)清楚了,在C#里也能從中汲取到充足的養(yǎng)分。但是,這還不是RAII的全部營(yíng)養(yǎng),RAII還有更多的擴(kuò)展用途。在 《Imperfect C++》一書中,Matthew Wilson展示了RAII的一種非常重要的應(yīng)用。為了不落個(gè)鸚鵡學(xué)舌的名聲,這里我給出一個(gè)真實(shí)遇到的案例,非常簡(jiǎn)單:我寫的程序需要響應(yīng)一個(gè)Grid 控件的CellTextChange事件,執(zhí)行一些運(yùn)算。在響應(yīng)這個(gè)事件(執(zhí)行運(yùn)算)的過(guò)程中,不能再響應(yīng)同一個(gè)事件,直到處理結(jié)束。為此,我設(shè)置了一個(gè) 標(biāo)志,用來(lái)控制事件響應(yīng):
        class MyForm
        
{
        
public:
            MyForm():is_cacul(
false{}
            ...
            
void OnCellTextChange(Cell& cell) {
                
if(is_cacul)
                    
return;
                is_cacul
=true;
                ... 
//執(zhí)行計(jì)算任務(wù)
                is_cacul=false;
            }

        
private:
            
bool is_cacul;
        }
;
    但是,這里的代碼不是異常安全的。如果在執(zhí)行計(jì)算的過(guò)程中拋出異常,那么is_cacul標(biāo)志將永遠(yuǎn)是true。此后,即便是正常的 CellTextChange也無(wú)法得到正確地響應(yīng)。同前面遇到的資源問(wèn)題一樣,傳統(tǒng)上我們不得不求助于try-catch語(yǔ)句。但是如果我們運(yùn)用 RAII,則可以使得代碼簡(jiǎn)化到不能簡(jiǎn)化,安全到不能再安全。我首先做了一個(gè)類:
        class BoolScope
        
{
        
public:
            BoolScope(
bool& val, bool newVal)
                :m_val(val), m_old(val) 
{
                m_val
=newVal;
            }

            
~BoolScope() {
                m_val
=m_old;
            }


        
private:
            
bool& m_val;
            
bool m_old;
        }
;
    這個(gè)類的作用是所謂“域守衛(wèi)(scoping)”,構(gòu)造函數(shù)接受兩個(gè)參數(shù):第一個(gè)是一個(gè)bool對(duì)象的引用,在構(gòu)造函數(shù)中保存在m_val成員里;第二個(gè) 是新的值,將被賦予傳入的那個(gè)bool對(duì)象。而該對(duì)象的原有值,則保存在m_old成員中。析構(gòu)函數(shù)則將m_old的值返還給m_val,也就是那個(gè) bool對(duì)象。有了這個(gè)類之后,便可以很優(yōu)雅地獲得異常安全:
        class MyForm
        
{
        
public:
            MyForm():is_cacul(
false{}
            ...
            
void OnCellTextChange(Cell& cell) {
                
if(is_cacul)
                    
return;
                BoolScope bs_(is_cacul, 
true);
                ... 
//執(zhí)行計(jì)算任務(wù)
            }

        
private:
            
bool is_cacul;
        }
;
    好啦,任務(wù)完成。在bs_創(chuàng)建的時(shí)候,is_cacul的值被替換成true,它的舊值保存在bs_對(duì)象中。當(dāng)OnCellTextChange()返回 時(shí),bs_對(duì)象會(huì)被自動(dòng)析構(gòu),析構(gòu)函數(shù)會(huì)自動(dòng)把保存起來(lái)的原值重新賦給is_cacul。一切又都回到原先的樣子。同樣,如果異常拋出,is_cacul 的值也會(huì)得到恢復(fù)。
    這個(gè)BoolScope可以在將來(lái)繼續(xù)使用,分?jǐn)傁聛?lái)的開(kāi)發(fā)成本幾乎是0。更進(jìn)一步,可以開(kāi)發(fā)一個(gè)通用的Scope模板,用于所有類型,就像《Imperfect C++》里的那樣。
    下面,讓我們把戰(zhàn)場(chǎng)轉(zhuǎn)移到C#,看看C#是如何實(shí)現(xiàn)域守衛(wèi)的??紤]到C#(.net)的對(duì)象模型的特點(diǎn),我們先實(shí)現(xiàn)引用類型的域守衛(wèi),然后再來(lái)看看如何對(duì)付值類型。其原因,一會(huì)兒會(huì)看到。
    我曾經(jīng)需要向一個(gè)grid中填入數(shù)據(jù),但是填入的過(guò)程中,控件不斷的刷新,造成閃爍,也影響性能,除非把控件上的AutoDraw屬性設(shè)為false。為此,我做了一個(gè)域守衛(wèi)類,在填寫操作之前關(guān)上AutoDraw,完成或異常拋出時(shí)再打開(kāi):
        class DrawScope : IDisposable
        
{
            
public DrawScope(Grid g, bool val) {
                m_grid
=g;
                m_old
=g->AutoDraw;
                m_grid
->AutoDraw=val;
            }

            
public void Dispose() {
                    g
->AutoDraw=m_old;
               }

            
private Grid m_grid;
            
private bool m_old;
        }
;
    于是,我便可以如下優(yōu)雅地處理AutoDraw屬性設(shè)置問(wèn)題:
        static void LoadData(Grid g) {
            
using(DrawScope ds=new DrawScope(g, false))
            
{
                ... 
//執(zhí)行數(shù)據(jù)裝載
            }

        }
    現(xiàn)在,我們回過(guò)頭,來(lái)實(shí)現(xiàn)值類型的域守衛(wèi)。案例還是采用前面的CellTextChange事件。當(dāng)我試圖著手對(duì)那個(gè)is_cacul執(zhí)行域守衛(wèi)時(shí),遇到了不小的麻煩。起初,我寫下了這樣的代碼:
        class BoolScope
        
{
            
private ??? m_val; //此處用什么類型?
            private bool m_old;
        }
;
    m_val應(yīng)當(dāng)是一個(gè)指向一個(gè)對(duì)象的引用,C#是沒(méi)有C++那些指針和引用的。在C#中,引用類型定義的對(duì)象實(shí)際上是一個(gè)指向?qū)ο蟮囊茫欢殿愋投x的 對(duì)象實(shí)際上是一個(gè)對(duì)象,或者說(shuō)“棧對(duì)象”,但卻沒(méi)有一種指向值類型的引用。(關(guān)于這種對(duì)象模型的優(yōu)劣,后面的“題外話”小節(jié)有一些探討)。我嘗試著采用兩 種辦法,一種不成功,而另一種成功了。
    C#(.net)有一種box機(jī)制,可以將一個(gè)值對(duì)象打包,放到堆中創(chuàng)建。這樣,或許可以把一個(gè)值對(duì)象編程引用對(duì)象,構(gòu)成C#可以引用的東西:
        class BoolScope : IDisposable
        
{
            
public BoolScope(object val, bool newVal) {
                    m_val
=val;                 //#1
                    m_old=(bool)val;
                    (
bool)m_val=newVal;    //#2
            }

            
public void Dispose() {
                    (
bool)m_val=m_old;    //#3
               }

            
private object m_val;
            
private bool m_old;
        }
    使用時(shí),應(yīng)當(dāng)采用如下形式:
        class MyForm
        
{
            
public MyForm() {
                is_cacul
=new bool(false); //boxing
            }

            ...
            
void OnCellTextChange(Cell& cell) {
                
if(is_cacul)
                    
return;
                
using(BoolScope bs=new BoolScope(is_cacul, true))
                
{
                    ... 
//執(zhí)行計(jì)算任務(wù)
                }

            }

            
private object is_cacul;
        }
;
    很可惜,此路不通。因?yàn)樵诖a#1的地方,并未執(zhí)行引用語(yǔ)義,而執(zhí)行了值語(yǔ)義。也就是說(shuō),沒(méi)有把val(它是個(gè)引用)的值賦給m_val(也是個(gè)引用), 而是為m_val做了個(gè)副本。以至于在代碼#2和#3處無(wú)法將newVal和m_old賦予val(也就是is_cacul)?;蛟SC#的設(shè)計(jì)者有無(wú)數(shù)理 由說(shuō)明這種設(shè)計(jì)的合理性,但是在這里,卻扼殺了一個(gè)非常有用的idom。而且,缺少對(duì)值對(duì)象的引用手段,大大限制了語(yǔ)言的靈活性和擴(kuò)展性。
    第二種方法就非常直白了,也絕對(duì)不應(yīng)當(dāng)出問(wèn)題,就是使用包裝類:
        class BoolVal
        
{
            
public BoolVal(bool v)
            
{
                m_val
=v;
            }

            
public bool getVal() {
                
return m_val;
            }

            
public void setVal(bool v) {
                m_val
=v;
            }

            
private bool m_val;
        }

        
class BoolScope : IDisposable
        
{
            
public IntScope(BoolVal iv, bool v)
            
{
                m_old 
= iv.getVal();
                m_Val 
= iv;
                m_Val.setVal(v);
            }

            
public virtual void Dispose()
            
{
                m_Val.setVal(m_old);
            }

            
private BoolVal m_Val;
            
private bool m_old;
        }
    這里,我做了一個(gè)包裝類BoolVal,是個(gè)引用類。然后以此為基礎(chǔ),編寫了一個(gè)BoolScope類。然后,便可以正常使用域守衛(wèi):
        class MyForm
        
{
            
public MyForm() {
                m_val.setVal(
false); //boxing
            }

            ...
            
void OnCellTextChange(Cell& cell) {
                
if(is_cacul)
                    
return;
                
using(BoolScope bs=new BoolScope(m_val, true))
                
{
                    ... 
//執(zhí)行計(jì)算任務(wù)
                }

            }

            
private BoolVal m_val;
        }
;
    好了,一切都很不錯(cuò)。盡管C#的對(duì)象模型給我們平添了不少麻煩,使得我多寫了不少代碼,但是使用域守衛(wèi)類仍然是一本萬(wàn)利的事情。作為GP fans,我當(dāng)然也嘗試著在C#里做一些泛型,以免去反復(fù)開(kāi)發(fā)包裝類和域守衛(wèi)類的苦惱。這些東西,就留給大家做練習(xí)吧。:)
    在某些場(chǎng)合下,我們可能會(huì)對(duì)一些對(duì)象做一些操作,完事后在恢復(fù)這個(gè)對(duì)象的原始狀態(tài),這也是域守衛(wèi)類的用武之地。只是守衛(wèi)一個(gè)結(jié)構(gòu)復(fù)雜的類,不是一件輕松的 工作。最直接的做法是取出所有的成員數(shù)據(jù),在結(jié)束后再重新復(fù)制回去。這當(dāng)然是繁復(fù)的工作,而且效率不高。但是,我們將在下一篇看到,如果運(yùn)用swap手 法,結(jié)合復(fù)制構(gòu)造函數(shù),可以很方便地實(shí)現(xiàn)這種域守衛(wèi)。這我們以后再說(shuō)。
    域守衛(wèi)作為RAII的一個(gè)擴(kuò)展應(yīng)用,非常簡(jiǎn)單,但卻極具實(shí)用性。如果我們對(duì)“資源”這個(gè)概念加以推廣,把一些值、狀態(tài)等等內(nèi)容都納入資源的范疇,那么域守衛(wèi)類的使用是順理成章的事。

題外話:C#的對(duì)象模型

    C#的設(shè)計(jì)理念是簡(jiǎn)化語(yǔ)言的學(xué)習(xí)和使用。但是,就前面案例中出現(xiàn)的問(wèn)題而言,在特定的情況下,特別是需要靈活和擴(kuò)展的時(shí)候,C#往往表現(xiàn)的差強(qiáng)人意。C# 的對(duì)象模型實(shí)際上是以堆對(duì)象和引用語(yǔ)義為核心的。不過(guò),考慮到維持堆對(duì)象的巨大開(kāi)銷和性能損失,應(yīng)用在一些簡(jiǎn)單的類型上,比如int、float等等,實(shí) 在得不嘗失。為此,C#將這些簡(jiǎn)單類型直接作為值處理,當(dāng)然也允許用戶定義自己的值類型。值類型擁有值語(yǔ)義。而值類型的本質(zhì)是棧對(duì)象,引用類型則是堆對(duì) 象。
    這樣看起來(lái)應(yīng)該是個(gè)不錯(cuò)的折中,但是實(shí)際上卻造成了不大不小的麻煩。前面的案例已經(jīng)明確地表現(xiàn)了這種對(duì)象模型引發(fā)的麻煩。由于C#拋棄值和引用的差異(為 了簡(jiǎn)化語(yǔ)言的學(xué)習(xí)和使用),那么對(duì)于一個(gè)引用對(duì)象,我們無(wú)法用值語(yǔ)義訪問(wèn)它;而對(duì)于一個(gè)值對(duì)象,我們無(wú)法用引用語(yǔ)義訪問(wèn)。對(duì)于前者,不會(huì)引發(fā)本質(zhì)性的問(wèn) 題,因?yàn)槲覀兛梢允褂贸蓡T函數(shù)來(lái)實(shí)現(xiàn)值語(yǔ)義。但是對(duì)于后者,則是無(wú)法逾越的障礙,就像在BoolScope案例中表現(xiàn)的那樣。在這種情況下,我們不得不用 引用類包裝值類型,使得值類型喪失了原有的性能和資源優(yōu)勢(shì)。
    更有甚者,C#的對(duì)象模型有時(shí)會(huì)造成語(yǔ)義上的沖突。由于值類型使用值語(yǔ)義,而引用類型使用引用語(yǔ)義。那么同樣是對(duì)象定義,便有可能使用不同的語(yǔ)義:
        int i, j=10;  //值類型
        i=j;            //值語(yǔ)義,兩個(gè)對(duì)象復(fù)制內(nèi)容
        i=5;           //i==5, j==10
        StringBuilder s1, s2 = new StringBuilder("s2");   //引用類型
        s1 = s2;        //引用語(yǔ)義,s1和s2指向同一個(gè)對(duì)象
        s1.Append(" is s1");    //s1==s2=="s1 is s2"
    同一個(gè)形式具有不同語(yǔ)義,往往會(huì)造成意想不到的問(wèn)題。比如,在軟件開(kāi)發(fā)的最初時(shí)刻,我們認(rèn)為某個(gè)類型是值類型就足夠了,還可以獲得性能上的好處。但是,隨 著項(xiàng)目進(jìn)入后期階段,發(fā)現(xiàn)最初的設(shè)計(jì)有問(wèn)題,值類型限制了該類型的某些特性(如不能擁有析構(gòu)函數(shù),不能引用等等),那么需要把它改成引用類型。于是便引發(fā) 一大堆麻煩,需要檢查所有使用該類型的代碼,然后把賦值操作改成復(fù)制操作。這肯定不是討人喜歡的工作。為此,在實(shí)際開(kāi)發(fā)中,很少自定義值類型,以免將來(lái)自縛手腳。于是,值類型除了語(yǔ)言內(nèi)置類型和.net庫(kù)預(yù)定義的類型外,成了一件擺設(shè)。
    相比之下,傳統(tǒng)語(yǔ)言,如Ada、C、C++、Pascal等,區(qū)分引用和值的做法盡管需要初學(xué)者花更多的精力理解其中的差別,但在使用中則更加妥善和安全。畢竟學(xué)習(xí)是暫時(shí)的,使用則是永遠(yuǎn)的。
posted on 2008-02-16 08:19 longshanks 閱讀(2073) 評(píng)論(2)  編輯 收藏 引用

Feedback

# re: C++的營(yíng)養(yǎng) 2008-02-16 11:47 abettor
用對(duì)照的方法學(xué)習(xí)確實(shí)是一個(gè)很不錯(cuò)的方法。  回復(fù)  更多評(píng)論
  

# re: C++的營(yíng)養(yǎng) 2008-02-22 16:45 i
file pfile=new file("my.txt");
應(yīng)該改為
file *pfile=new file("my.txt");  回復(fù)  更多評(píng)論
  

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            一本大道久久精品懂色aⅴ| 国语自产精品视频在线看抢先版结局 | 亚洲精品九九| 免费亚洲一区| 日韩写真在线| 99视频热这里只有精品免费| 国产精品第一区| 久久成人在线| 久久蜜桃资源一区二区老牛| 亚洲国产日韩欧美在线图片| 亚洲精品色婷婷福利天堂| 欧美日韩1区2区| 午夜精品一区二区三区在线| 欧美亚洲在线观看| 亚洲国产另类久久精品| 一本色道久久综合一区| 国产免费成人在线视频| 麻豆国产精品一区二区三区| 欧美成人有码| 欧美日本久久| 香蕉视频成人在线观看| 一区二区三区在线看| 欧美激情一区二区三区高清视频| 欧美国产日韩精品免费观看| 午夜一区二区三区不卡视频| 久久婷婷麻豆| 亚洲欧美另类久久久精品2019| 久久久久高清| 亚洲午夜av| 老司机成人网| 欧美怡红院视频一区二区三区| 欧美成人dvd在线视频| 午夜在线视频观看日韩17c| 久久人人爽爽爽人久久久| 亚洲尤物视频网| 美国十次了思思久久精品导航| 午夜精品一区二区在线观看| 欧美国产日韩在线| 久久综合色88| 国产精品一区二区久久国产| 亚洲国产一区二区三区a毛片 | 正在播放欧美视频| 亚洲狠狠婷婷| 久久gogo国模啪啪人体图| 亚洲午夜久久久久久久久电影院| 久久久综合免费视频| 久久se精品一区精品二区| 欧美日韩另类一区| 亚洲国产精品一区二区www在线| 国产亚洲一区二区三区在线播放| 日韩午夜电影av| 亚洲美洲欧洲综合国产一区| 久久影视三级福利片| 久久精品夜色噜噜亚洲aⅴ| 国产精品va在线播放我和闺蜜| 亚洲激情一区二区三区| 亚洲电影第三页| 久久裸体艺术| 久久久水蜜桃| 国一区二区在线观看| 久久国产88| 巨胸喷奶水www久久久免费动漫| 国产精品亚发布| 午夜精品久久久久久99热软件 | 亚洲精品乱码久久久久久日本蜜臀| 久久精品国产一区二区三区| 久久久人成影片一区二区三区| 国产日韩欧美制服另类| 欧美伊人久久大香线蕉综合69| 欧美在线观看网址综合| 国产日韩欧美一区在线| 久久精品欧洲| 欧美国产精品日韩| 亚洲精品中文字幕在线观看| 欧美伦理一区二区| 亚洲精品乱码| 亚洲男人第一网站| 国产日韩欧美一区二区三区四区| 欧美在线免费一级片| 麻豆成人综合网| 亚洲蜜桃精久久久久久久| 欧美日韩免费看| 亚洲一区二区三区久久| 久久久久91| 久久精品99| 亚洲自拍都市欧美小说| 国产精品日韩欧美| 欧美亚洲视频| 欧美国产日韩精品| 亚洲视频电影图片偷拍一区| 国产欧美精品日韩区二区麻豆天美| 久久精彩免费视频| 亚洲国产精品久久| 亚洲专区在线| 亚洲丶国产丶欧美一区二区三区| 欧美日韩另类在线| 亚欧美中日韩视频| 亚洲高清一区二| 午夜一区二区三区不卡视频| 亚洲福利在线视频| 国产精品久久777777毛茸茸| 久久精品国产77777蜜臀| 亚洲国产欧美日韩| 欧美在线一二三四区| 亚洲二区免费| 国产毛片一区| 欧美日韩国产成人精品| 欧美一区二区视频在线| 亚洲人成在线观看| 久久久青草婷婷精品综合日韩 | 国产欧美精品久久| 欧美黄色免费| 久久精品视频播放| 亚洲午夜精品久久久久久app| 欧美xart系列高清| 欧美专区亚洲专区| 亚洲神马久久| 亚洲欧洲在线视频| 韩国免费一区| 国产精品亚洲视频| 欧美日韩国产麻豆| 免费一区视频| 久久久噜久噜久久综合| 亚洲一区在线观看视频| 亚洲精品偷拍| 亚洲第一偷拍| 嫩草成人www欧美| 久久精品夜色噜噜亚洲aⅴ| 亚洲一区二区动漫| 99在线热播精品免费| 亚洲激情在线观看视频免费| 国产一区二区三区日韩| 国产美女精品视频免费观看| 国产精品黄色在线观看| 欧美日韩中文在线观看| 欧美理论电影在线观看| 欧美国产在线电影| 蜜臀av性久久久久蜜臀aⅴ| 久久久久看片| 久久婷婷丁香| 久久女同互慰一区二区三区| 久久精品盗摄| 久久久久青草大香线综合精品| 欧美一区二区视频观看视频| 欧美在线91| 久久精品国产清高在天天线 | 亚洲一区影音先锋| 在线亚洲欧美专区二区| 亚洲视频1区2区| 亚洲免费在线视频| 久久精品久久99精品久久| 久久久久久97三级| 蜜臀av性久久久久蜜臀aⅴ四虎| 美女主播精品视频一二三四| 欧美激情在线观看| 欧美三级日本三级少妇99| 国产美女精品免费电影| 欧美成在线视频| 亚洲一区二区三区在线| 亚洲精品视频在线播放| 亚洲五月六月| 久久爱www久久做| 久久久蜜桃精品| 欧美黄污视频| 国产精品久久久久久户外露出| 国产日韩欧美一区二区三区四区 | 国产精品日韩一区二区| 韩国成人精品a∨在线观看| 亚洲第一久久影院| 一区二区高清视频| 亚洲欧美经典视频| 久久久久国内| 母乳一区在线观看| 在线视频亚洲一区| 亚洲欧美日韩精品久久久久| 性做久久久久久久久| 欧美高清视频一区| 欧美视频在线观看 亚洲欧| 国产精品视频午夜| 亚洲片区在线| 亚洲综合二区| 久久国产视频网| 亚洲免费黄色| 午夜亚洲福利在线老司机| 久久国产精品久久久| 香蕉久久一区二区不卡无毒影院 | 亚洲一级黄色av| 亚洲精品视频啊美女在线直播| 亚洲精品专区| 亚洲免费影院| 久久久精品五月天| 欧美不卡在线视频| 久久精品国产一区二区三区免费看 | 久久激情一区| 男人的天堂亚洲在线| 亚洲一区在线播放| 久久夜色精品亚洲噜噜国产mv | 午夜国产精品影院在线观看| 久久久久久久久久久久久女国产乱 | 日韩亚洲欧美一区|