• <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>

            sherrylso

            C++博客 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
              18 Posts :: 0 Stories :: 124 Comments :: 0 Trackbacks

            四、c++中的多態(tài)規(guī)則。
            一) c++中函數(shù)動(dòng)態(tài)綁定規(guī)則。
            看下面的例子:

            class Window
            {

            public:
              virtual 
            void  oops()
              
            {
                cout
            <<"Window oops"<<endl;
              }

            public:
              
            int height;
              
            int width;
            }
            ;
            class TextWindow : public Window
            {

            public:
              virtual 
            void  oops()
              
            {
                cout
            <<"TextWindow oops"<<cursorLocation<<endl;
              }

            public:
              
            int cursorLocation;
            }
            ;

            main()
            {
              Window win;
              Window
            * tWin;
              TextWindow 
            * tWinPtr;

              tWinPtr 
            = new TextWindow;
              tWin 
            = tWinPtr;
              
              win.oops();
              tWin
            ->oops();
            }


            類TextWindow繼承與類Window。我想程序運(yùn)行的結(jié)果,大多數(shù)熟悉C++的人都會(huì)知道,
            win.oops()最終調(diào)用的是父類oops函數(shù),而tWin->oops()調(diào)用的是子類TextWindow的函數(shù)。
            通過(guò)這個(gè)例子,我們先總結(jié)一下c++中的多態(tài)調(diào)用規(guī)則
            第一、對(duì)于指針和引用類型,當(dāng)消息調(diào)用的成員函數(shù)有可能被重寫時(shí),最終被選擇調(diào)用的成員函數(shù)由消息接收者的動(dòng)態(tài)類型確定(注意:在OO概念中,對(duì)某個(gè)對(duì)象成員函數(shù)進(jìn)行調(diào)用,常常稱為給該對(duì)象發(fā)送消息,該對(duì)象就是消息的接收者)。
                  如上例:tWin->oops(),由于tWin的動(dòng)態(tài)類型是子類TextWindow,而不是Windows,所以tWin->oops()調(diào)用的是子類TextWindow的函數(shù)。
            二、對(duì)于其它的變量,對(duì)虛擬函數(shù)調(diào)用綁定完全由該變量的靜態(tài)類型確定(即該變量的聲明),而不是該變量的真實(shí)類型確定。
                 如上例:win.oops(),由于win的聲明類型為Window類,所以其結(jié)果調(diào)用的是父類oops函數(shù)。
            二) 探討。
                 接下來(lái),我們要看的問(wèn)題是,在c++中,為什么對(duì)于多態(tài)規(guī)則(或者說(shuō)是動(dòng)態(tài)函數(shù)綁定規(guī)則),做出了兩中不同的劃分,即:只有指針與引用類型,才進(jìn)行函數(shù)的后期動(dòng)態(tài)綁定,也就是多態(tài)。這或許也是許多c++初學(xué)者非常迷惑的地方。這種規(guī)則的不一致性,的確給c++的語(yǔ)法造成一定的復(fù)雜性。而這在Java,或者C#中是沒(méi)有的,后面我們會(huì)涉及到。
                  我們先來(lái)看例子。 

            void f()
            {
              Window  win;
              Window
            * tWinPtr;
              
              tWinPtr 
            = new TextWindow;
              win     
            = *tWinPtr;//what's problem happen
               
              win.oops(); 
            //what's problem happen
              tWinPtr->oops();
            }


                  在這里,如果我們假設(shè),c++的函數(shù)動(dòng)態(tài)綁定規(guī)則是一致的,看看會(huì)發(fā)生什么問(wèn)題???
                  現(xiàn)在win被聲明為Window類型,然而其真實(shí)的類型為TextWindow(因?yàn)閣in=*tWinPtr),由于我們的假設(shè),win現(xiàn)在是允許進(jìn)行動(dòng)態(tài)函數(shù)綁定的,所以當(dāng)執(zhí)行win.oops()時(shí),實(shí)際上是調(diào)用子類TextWindow的成員函數(shù)。
                 現(xiàn)在,我們有必要來(lái)審視一下win變量的內(nèi)存布局。由于win變量是在棧上聲明的變量,其內(nèi)存也是從棧進(jìn)行分配(這是c++從c語(yǔ)言那里繼承過(guò)來(lái)的優(yōu)良特質(zhì),從棧上分配內(nèi)存空間比動(dòng)態(tài)分配內(nèi)存有更好的執(zhí)行速度),c++標(biāo)準(zhǔn)規(guī)定:給win變量分配內(nèi)存空間的大小,由其靜態(tài)的類型確定,即應(yīng)該是Window類所使用的內(nèi)存空間大小。在這種情況下,當(dāng)執(zhí)行win=*tWinPtr時(shí),什么會(huì)發(fā)生?如下圖:

            在默認(rèn)的拷貝構(gòu)造函數(shù)情況下,信息會(huì)出現(xiàn)丟失,這就是著名的slicing off現(xiàn)象。結(jié)果,變量cursorLocation在win的內(nèi)存空間里丟失了。然而,問(wèn)題是:在我們假設(shè)下,我們要求win.oops()導(dǎo)致TextWindow的成員函數(shù)調(diào)用,而在這個(gè)函數(shù)中,訪問(wèn)到的cursorLocation變量是不存在!win.oops()調(diào)用將導(dǎo)致內(nèi)存違例!
                  到這里,我們可以總結(jié)一下:c++標(biāo)準(zhǔn)基于的其特定的內(nèi)存分配規(guī)則,給出了以上,我們?cè)谇耙还?jié)總結(jié)出的函數(shù)動(dòng)態(tài)綁定規(guī)則。
            三) 深入。
                  當(dāng)然,我們也可以說(shuō),c++也可以通過(guò)改變其內(nèi)存分配規(guī)則,來(lái)給出一個(gè)一致性的函數(shù)動(dòng)態(tài)綁定規(guī)則。比如:可以考慮在給win變量分配內(nèi)存空間時(shí),考慮其所有子類需求,然后分配最大數(shù)量的內(nèi)存空間給win變量。這種做法可行性很差,對(duì)于編譯器而言,需要掃描整個(gè)程序(確定該類的所有子類),才能確定最大的內(nèi)存空間是多少。在使用類庫(kù)或者框架的情況下,會(huì)引起整個(gè)類庫(kù),框架的重新編譯,這是得不償失的!而這種做法,在oo的語(yǔ)言中,基本上是沒(méi)有的。這也是c++不得不基于其現(xiàn)有的內(nèi)存管理機(jī)制,而對(duì)多態(tài)規(guī)則作出的不一致的解釋。
                對(duì)于這個(gè)c++現(xiàn)有的內(nèi)存管理機(jī)制,我們?nèi)绻麖牧硗饨嵌热ダ斫獾脑?是很合理的。當(dāng)win=*tWinPtr發(fā)生
            時(shí),我們可以類似地認(rèn)為:好比一個(gè)float類型的數(shù)賦給了一個(gè)interger類型的變量,其結(jié)果當(dāng)然是float的值被截?cái)嗔恕?br>    我們?cè)賮?lái)看其它語(yǔ)言,Java(或者C#)是怎么解決的。
                最重要的一點(diǎn)是,在Java(C#)中只有引用的概念,所以在棧上聲明的類的變量,只需要分配一個(gè)指針大小的內(nèi)存空間就行了,而不需要給該變量分配空間來(lái)保存變量?jī)?nèi)容本身,這其實(shí)就是我們現(xiàn)在看到的c++中指針和引用的情況。
                

             

            posted on 2008-01-05 23:12 愛(ài)上龍卷風(fēng) 閱讀(3194) 評(píng)論(8)  編輯 收藏 引用

            Feedback

            # re: c語(yǔ)法背后的故事(二) [未登錄](méi) 2008-01-06 12:21 ZZZ
            是有點(diǎn)晦澀  回復(fù)  更多評(píng)論
              

            # re: c++晦澀語(yǔ)法背后的故事(二) 2008-01-07 22:53 abettor.org
            看來(lái)這個(gè)容易理解一些,呵呵。  回復(fù)  更多評(píng)論
              

            # re: c++晦澀語(yǔ)法背后的故事(二) [未登錄](méi) 2008-01-10 11:10 Composition
            sherrylso兄,
            void f()
            {
            Window win;
            Window* tWinPtr;

            tWinPtr = new TextWindow;
            win = *tWinPtr;//what's problem happen

            win.oops(); //what's problem happen
            tWinPtr->oops();
            }
            中win.oops(); 仍然會(huì)調(diào)用父類的方法,因?yàn)榫幾g器生成的拷貝構(gòu)造函數(shù)不會(huì)拷貝vptr。希望你check一下。
            這里僅僅切割了子對(duì)象,但是切割后的對(duì)象是合法的父對(duì)象,除非你的設(shè)計(jì)不合理,違反了LSP。  回復(fù)  更多評(píng)論
              

            # re: c++晦澀語(yǔ)法背后的故事(二) 2008-01-11 22:04 愛(ài)上龍卷風(fēng)
            @Composition
            我的論述的前提是:“假設(shè):c++的函數(shù)動(dòng)態(tài)綁定規(guī)則是一致的“
            搞不清楚你的觀點(diǎn)是什么?
              回復(fù)  更多評(píng)論
              

            # re: c++晦澀語(yǔ)法背后的故事(二) 2008-01-14 13:35 Composition
            我的意思是不管子對(duì)象怎么切割,win.oops(); 永遠(yuǎn)會(huì)調(diào)用父類的函數(shù),絕不會(huì)因?yàn)榍懈钫{(diào)用子類的方法。既然不調(diào)用子類的方法,自然不會(huì)出錯(cuò)。你可以在編譯器實(shí)驗(yàn)一下,你這個(gè)例子會(huì)輸出Window oops xxx,沒(méi)有錯(cuò)誤。
            不管怎么假設(shè),通過(guò)對(duì)象調(diào)用成員函數(shù)都是編譯時(shí)綁定,永遠(yuǎn)不可能調(diào)用到子類的成員函數(shù)。
            win = *tWinPtr;這句會(huì)調(diào)用Window的拷貝構(gòu)造(編譯器生成),但是拷貝構(gòu)造不會(huì)拷貝對(duì)象的vptr。而且就算拷貝了,通過(guò)對(duì)象調(diào)用成員函數(shù)也不通過(guò)vptr找vtable,而是直接調(diào)用對(duì)象類型對(duì)應(yīng)到成員函數(shù)(編譯后成為全局函數(shù))。你這里假設(shè)“c++的函數(shù)動(dòng)態(tài)綁定規(guī)則是一致的”,但別忘了,vptr不拷貝,這是C++實(shí)現(xiàn)機(jī)制,怎么調(diào)用都是父類的成員函數(shù),不會(huì)有多態(tài)。
            如果你做出拷貝構(gòu)造會(huì)拷貝vptr+對(duì)象調(diào)用會(huì)查找vtable的假設(shè),那么你的論述應(yīng)該合理。但是這么假設(shè)個(gè)人認(rèn)為意義不大。  回復(fù)  更多評(píng)論
              

            # re: c++晦澀語(yǔ)法背后的故事(二) 2008-01-14 21:39 愛(ài)上龍卷風(fēng)
            @Composition
            1) 首先,我要論述的是多態(tài)規(guī)則,不是只有C++有。很多面向?qū)ο蟮恼Z(yǔ)言都有的特性。Java, C#, Smalltalk。你不必要局限于C++本身。
            2) 其次,你說(shuō)的,我都贊同,這就是c++的內(nèi)存管理機(jī)制。
            3) 基于c++的內(nèi)存管理機(jī)制,推出了現(xiàn)有的C++多態(tài)規(guī)則。
            4)我是在討論,C++語(yǔ)法為什么復(fù)雜的背后原因。也就是怎么去更好地理解現(xiàn)行的語(yǔ)法。
              回復(fù)  更多評(píng)論
              

            # re: c++晦澀語(yǔ)法背后的故事(二) 2008-01-14 21:41 愛(ài)上龍卷風(fēng)
            @Composition
            再者,如果C++改變了現(xiàn)有的內(nèi)存管理機(jī)制,即我提到的:可以考慮在給win變量分配內(nèi)存空間時(shí),考慮其所有子類需求,然后分配最大數(shù)量的內(nèi)存空間給win變量。
            是可以做到多態(tài)規(guī)則的一致性  回復(fù)  更多評(píng)論
              

            # re: c++晦澀語(yǔ)法背后的故事(二) 2008-01-15 12:14 Composition
            我明白你的意思了,不過(guò)我也沒(méi)有完全拘泥于C++,但是C++的多態(tài)實(shí)現(xiàn)確實(shí)與其他語(yǔ)言很不同。如Python,Ruby之類語(yǔ)言通過(guò)給類建立哈希表來(lái)實(shí)現(xiàn)多態(tài)。Java,C#之類應(yīng)該是通過(guò)元數(shù)據(jù)。而且只有C++才有切割問(wèn)題,其他語(yǔ)言在堆上分配就不存在切割了。
            至于你的例子f()我覺(jué)得可以改成如下的例子更能說(shuō)明問(wèn)題,而且也不必做過(guò)多假設(shè):
            void f()
            {
            Window win;
            TextWindow textWin;
            Window* tWinPtr;

            tWinPtr = new TextWindow;
            memcpy(reinterpret_cast<char*>(tWinPtr) + 4,
            reinterpret_cast<char*>(&win) + 4,
            sizeof(win) - 4); //don't change vptr

            tWinPtr->oops(); //what's problem happen
            }
            這個(gè)例子會(huì)發(fā)生錯(cuò)誤,即使運(yùn)行不出錯(cuò),邏輯也是錯(cuò)的。
            至于你對(duì)“可以考慮在給win變量分配內(nèi)存空間時(shí),考慮其所有子類需求,然后分配最大數(shù)量的內(nèi)存空間給win變量。”的論述很透徹,我同意。  回復(fù)  更多評(píng)論
              


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            国内精品久久久久久野外| 久久93精品国产91久久综合| 无码精品久久久天天影视| 久久国产精品一国产精品金尊| 久久青青草原综合伊人| 色综合久久88色综合天天 | 国产精品99精品久久免费| 久久久久久久综合日本| 亚洲综合熟女久久久30p| 国产99久久久久久免费看| 婷婷五月深深久久精品| 人妻中文久久久久| 激情五月综合综合久久69| 久久久久无码精品国产| 亚洲国产成人乱码精品女人久久久不卡 | 久久亚洲欧美国产精品 | 女人香蕉久久**毛片精品| 人妻无码精品久久亚瑟影视| 国产午夜久久影院| 久久精品无码午夜福利理论片| 色综合合久久天天给综看| 欧美激情精品久久久久久| 99热都是精品久久久久久| 国产亚洲精久久久久久无码| 国内精品人妻无码久久久影院导航| 精品无码久久久久久久久久| 久久精品国产亚洲一区二区| 亚洲国产精品无码久久SM| 精品国产乱码久久久久久人妻| 日本亚洲色大成网站WWW久久| 国产精品成人久久久久久久| 国产激情久久久久影院小草| 久久91这里精品国产2020| 久久av免费天堂小草播放| 久久se精品一区二区影院 | 少妇人妻88久久中文字幕| 2021国内精品久久久久久影院| 欧美色综合久久久久久| 色诱久久av| 色诱久久久久综合网ywww| 国内精品伊人久久久久av一坑|