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

            麒麟子

            ~~

            導航

            <2025年6月>
            25262728293031
            1234567
            891011121314
            15161718192021
            22232425262728
            293012345

            統(tǒng)計

            常用鏈接

            留言簿(12)

            隨筆分類

            隨筆檔案

            Friends

            WebSites

            積分與排名

            最新隨筆

            最新評論

            閱讀排行榜

            評論排行榜

            #

            c++虛函數(shù)表探究

            C++中的虛函數(shù)的作用主要是實現(xiàn)了多態(tài)的機制。關(guān)于多態(tài),簡而言之就是用父類型別的指針指向其子類的實例,然后通過父類的指針調(diào)用實際子類的成員函數(shù)。這種技術(shù)可以讓父類的指針有“多種形態(tài)”,這是一種泛型技術(shù)。所謂泛型技術(shù),說白了就是試圖使用不變的代碼來實現(xiàn)可變的算法。比如:模板技術(shù),RTTI技術(shù),虛函數(shù)技術(shù),要么是試圖做到在編譯時決議,要么試圖做到運行時決議。
            關(guān)于虛函數(shù)的使用方法,我在這里不做過多的闡述。大家可以看看相關(guān)的C++的書籍。在這篇文章中,我只想從虛函數(shù)的實現(xiàn)機制上面為大家 一個清晰的剖析。
            當然,相同的文章在網(wǎng)上也出現(xiàn)過一些了,但我總感覺這些文章不是很容易閱讀,大段大段的代碼,沒有圖片,沒有詳細的說明,沒有比較,沒有舉一反三。不利于學習和閱讀,所以這是我想寫下這篇文章的原因。也希望大家多給我提意見。
            言歸正傳,讓我們一起進入虛函數(shù)的世界。
            虛函數(shù)表
            對C++ 了解的人都應該知道虛函數(shù)(Virtual Function)是通過一張?zhí)摵瘮?shù)表(Virtual Table)來實現(xiàn)的。簡稱為V-Table。 在這個表中,主是要一個類的虛函數(shù)的地址表,這張表解決了繼承、覆蓋的問題,保證其容真實反應實際的函數(shù)。這樣,在有虛函數(shù)的類的實例中這個表被分配在了 這個實例的內(nèi)存中,所以,當我們用父類的指針來操作一個子類的時候,這張?zhí)摵瘮?shù)表就顯得由為重要了,它就像一個地圖一樣,指明了實際所應該調(diào)用的函數(shù)。
            這里我們著重看一下這張?zhí)摵瘮?shù)表。在C++的標準規(guī)格說明書中說到,編譯器必需要保證虛函數(shù)表的指針存在于對象實例中最前面的位置(這是為了保證正確取到虛函數(shù)的偏移量)。 這意味著我們通過對象實例的地址得到這張?zhí)摵瘮?shù)表,然后就可以遍歷其中函數(shù)指針,并調(diào)用相應的函數(shù)。
            聽我扯了那么多,我可以感覺出來你現(xiàn)在可能比以前更加暈頭轉(zhuǎn)向了。 沒關(guān)系,下面就是實際的例子,相信聰明的你一看就明白了。
            假設(shè)我們有這樣的一個類:
            class Base {
            public:
            virtual void f() { cout << "Base::f" << endl; }
            virtual void g() { cout << "Base::g" << endl; }
            virtual void h() { cout << "Base::h" << endl; }
            };
            按照上面的說法,我們可以通過Base的實例來得到虛函數(shù)表。 下面是實際例程:
            typedef void(*Fun)(void);
            Base b;
            Fun pFun = NULL;
            cout << "虛函數(shù)表地址:" << (int*)(&b) << endl;
            cout << "虛函數(shù)表 — 第一個函數(shù)地址:" << (int*)*(int*)(&b) << endl;
            // Invoke the first virtual function
            pFun = (Fun)*((int*)*(int*)(&b));
            pFun();
            實際運行經(jīng)果如下:(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)
            虛函數(shù)表地址:0012FED4
            虛函數(shù)表 — 第一個函數(shù)地址:0044F148
            Base::f
            通過這個示例,我們可以看到,我們可以通過強行把&b轉(zhuǎn)成int *,取得虛函數(shù)表的地址,然后,再次取址就可以得到第一個虛函數(shù)的地址了,也就是Base::f(),這在上面的程序中得到了驗證(把int* 強制轉(zhuǎn)成了函數(shù)指針)。通過這個示例,我們就可以知道如果要調(diào)用Base::g()和Base::h(),其代碼如下:
            (Fun)*((int*)*(int*)(&b)+0); // Base::f()
            (Fun)*((int*)*(int*)(&b)+1); // Base::g()
            (Fun)*((int*)*(int*)(&b)+2); // Base::h()
            這個時候你應該懂了吧。什么?還是有點暈。也是,這樣的代碼看著太亂了。沒問題,讓我畫個圖解釋一下。如下所示:


            注意:在上面這個圖中,我在虛函數(shù)表的最后多加了一個結(jié)點,這是虛函數(shù)表的結(jié)束結(jié)點,就像字符串的結(jié)束符“\0”一樣,其標志了虛函數(shù)表的結(jié)束。這個結(jié)束標志的值在不同的編譯器下是不同的。在WinXP+VS2003下,這個值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,這個值是如果1,表示還有下一個虛函數(shù)表,如果值是0,表示是最后一個虛函數(shù)表。
            下面,我將分別說明“無覆蓋”和“有覆蓋”時的虛函數(shù)表的樣子。沒有覆蓋父類的虛函數(shù)是毫無意義的。我之所以要講述沒有覆蓋的情況,主要目的是為了給一個對比。在比較之下,我們可以更加清楚地知道其內(nèi)部的具體實現(xiàn)。
            一般繼承(無虛函數(shù)覆蓋)
            下面,再讓我們來看看繼承時的虛函數(shù)表是什么樣的。假設(shè)有如下所示的一個繼承關(guān)系:
             

            請注意,在這個繼承關(guān)系中,子類沒有重載任何父類的函數(shù)。那么,在派生類的實例中,其虛函數(shù)表如下所示:
            對于實例:Derive d; 的虛函數(shù)表如下:

             

             

            我們可以看到下面幾點:
            1)虛函數(shù)按照其聲明順序放于表中。
            2)父類的虛函數(shù)在子類的虛函數(shù)前面。
            我相信聰明的你一定可以參考前面的那個程序,來編寫一段程序來驗證。
            一般繼承(有虛函數(shù)覆蓋)
            覆蓋父類的虛函數(shù)是很顯然的事情,不然,虛函數(shù)就變得毫無意義。下面,我們來看一下,如果子類中有虛函數(shù)重載了父類的虛函數(shù),會是一個什么樣子?假設(shè),我們有下面這樣的一個繼承關(guān)系。

            為了讓大家看到被繼承過后的效果,在這個類的設(shè)計中,我只覆蓋了父類的一個函數(shù):f()。那么,對于派生類的實例,其虛函數(shù)表會是下面的一個樣子:
             

            我們從表中可以看到下面幾點,
            1)覆蓋的f()函數(shù)被放到了虛表中原來父類虛函數(shù)的位置。
            2)沒有被覆蓋的函數(shù)依舊。
            這樣,我們就可以看到對于下面這樣的程序,
            Base *b = new Derive();
            b->f();
            由b所指的內(nèi)存中的虛函數(shù)表的f()的位置已經(jīng)被Derive::f()函數(shù)地址所取代,于是在實際調(diào)用發(fā)生時,是Derive::f()被調(diào)用了。這就實現(xiàn)了多態(tài)。
            多重繼承(無虛函數(shù)覆蓋)
            下面,再讓我們來看看多重繼承中的情況,假設(shè)有下面這樣一個類的繼承關(guān)系。注意:子類并沒有覆蓋父類的函數(shù)。


            對于子類實例中的虛函數(shù)表,是下面這個樣子:

             


            我們可以看到:
            1) 每個父類都有自己的虛表。
            2) 子類的成員函數(shù)被放到了第一個父類的表中。(所謂的第一個父類是按照聲明順序來判斷的)
            這樣做就是為了解決不同的父類類型的指針指向同一個子類實例,而能夠調(diào)用到實際的函數(shù)。
            多重繼承(有虛函數(shù)覆蓋)
            下面我們再來看看,如果發(fā)生虛函數(shù)覆蓋的情況。
            下圖中,我們在子類中覆蓋了父類的f()函數(shù)。

            下面是對于子類實例中的虛函數(shù)表的圖:
             

            我們可以看見,三個父類虛函數(shù)表中的f()的位置被替換成了子類的函數(shù)指針。這樣,我們就可以任一靜態(tài)類型的父類來指向子類,并調(diào)用子類的f()了。如:
            Derive d;
            Base1 *b1 = &d;
            Base2 *b2 = &d;
            Base3 *b3 = &d;
            b1->f(); //Derive::f()
            b2->f(); //Derive::f()
            b3->f(); //Derive::f()
            b1->g(); //Base1::g()
            b2->g(); //Base2::g()
            b3->g(); //Base3::g()
            安全性
            每次寫C++的文章,總免不了要批判一下C++。這篇文章也不例外。通過上面的講述,相信我們對虛函數(shù)表有一個比較細致的了解了。水可載舟,亦可覆舟。下面,讓我們來看看我們可以用虛函數(shù)表來干點什么壞事吧。
            一、通過父類型的指針訪問子類自己的虛函數(shù)
            我們知道,子類沒有重載父類的虛函數(shù)是一件毫無意義的事情。因為多態(tài)也是要基于函數(shù)重載的。雖然在上面的圖中我們可以看到Base1的虛表中有Derive的虛函數(shù),但我們根本不可能使用下面的語句來調(diào)用子類的自有虛函數(shù):
            Base1 *b1 = new Derive();
            b1->f1(); //編譯出錯
            任何妄圖使用父類指針想調(diào)用子類中的未覆蓋父類的成員函數(shù)的行為都會被編譯器視為非法,所以,這樣的程序根本無法編譯通過。但在運行時,我們可以通過指針的方式訪問虛函數(shù)表來達到違反C++語義的行為。(關(guān)于這方面的嘗試,通過閱讀后面附錄的代碼,相信你可以做到這一點)
            二、訪問non-public的虛函數(shù)
            另外,如果父類的虛函數(shù)是private或是protected的,但這些非public的虛函數(shù)同樣會存在于虛函數(shù)表中,所以,我們同樣可以使用訪問虛函數(shù)表的方式來訪問這些non-public的虛函數(shù),這是很容易做到的。
            如:
            class Base {
            private:
            virtual void f() { cout << "Base::f" << endl; }
            };
            class Derive : public Base{
            };
            typedef void(*Fun)(void);
            void main() {
            Derive d;
            Fun pFun = (Fun)*((int*)*(int*)(&d)+0);
            pFun();
            }

            posted @ 2009-07-24 23:52 麒麟子 閱讀(357) | 評論 (0)編輯 收藏

            [轉(zhuǎn)]D3D中的渲染到紋理

            D3D中的渲染到紋理

            http://www.cnblogs.com/flying_bat/

            渲染到紋理是D3D中的一項高級技術(shù)。一方面,它很簡單,另一方面它很強大并能產(chǎn)生很多特殊效果。 比如說發(fā)光效果,環(huán)境映射,陰影映射,都可以通過它來實現(xiàn)。渲染到紋理只是渲染到表面的一個延伸。我們只需再加些東西就可以了。首先,我們要創(chuàng)造一個紋理,并且做好一些防范措施。第二步我們就可以把適當?shù)膱鼍颁秩镜轿覀儎?chuàng)建的紋理上了。然后,我們把這個紋理用在最后的渲染上。
              ?main.cpp
              首先我們得聲明所需要的對象。當然我們需要一張用來渲染的紋理。此外,我們還需要兩個Surface對象。一個是用來存儲后臺緩沖區(qū),一個用來當紋理的渲染對象。后面我再詳細介紹它們。另外我們還需要兩個矩陣,一個是用來當紋理的投影矩陣,另一個是存儲原來的矩陣。
              LPDIRECT3DTEXTURE9 pRenderTexture = NULL;
              LPDIRECT3DSURFACE9 pRenderSurface = NULL,pBackBuffer = NULL;
              D3DXMATRIX matProjection,matOldProjection;
              現(xiàn)在我們來創(chuàng)建紋理。前兩個參數(shù)是紋理的寬度和高度,第三個參數(shù)是紋理的多級漸進紋理序列參數(shù),在這里是設(shè)為1,第四個參數(shù)非常重要而且必須設(shè)為D3DUSAGE_RENDERTARGET,表明我們所創(chuàng)建的紋理是用來渲染的。剩下的參數(shù)就是指紋理格式,頂點緩沖區(qū)的內(nèi)存位置,和一個指向紋理的指針。當紋理是用來當渲染對象時,頂點緩沖區(qū)的內(nèi)存位置必須設(shè)為D3D_DEFAILT。
              g_App.GetDevice()->CreateTexture(256,256,1,D3DUSAGE_RENDERTARGET,D3DFMT_R5G6B5,D3DPOOL_DEFAULT,&pRenderTexture,NULL);
              為了訪問紋理內(nèi)存對象,我們需要一個Surface對象,因為D3D中的紋理是用這樣的一個Surface來存儲紋理數(shù)據(jù)的。為了得到紋理表面的Surface,我們需要調(diào)用方法GetSurfaceLevel() 。第一個參數(shù)我們設(shè)為0,第二個參數(shù)為一個指向surface對象的指針。
              pRenderTexture->GetSurfaceLevel(0,&pRenderSurface);
              下一步就是創(chuàng)建一個適合紋理維數(shù)的投影矩陣,因為紋理的橫縱比和后臺緩沖區(qū)的不一樣。
              D3DXMatrixPerspectiveFovLH(&matProjection,D3DX_PI / 4.0f,1,1,100);
              在我們的循環(huán)渲染之前,我們必須保存后臺緩沖區(qū)和它的投影矩陣。
              g_App.GetDevice()->GetTransform(D3DTS_PROJECTION,&matOldProjection);
              g_App.GetDevice()->GetRenderTarget(0,&pBackBuffer);
              渲染循環(huán)函數(shù)可以分為兩個部分。第一部分是渲染到紋理的過程。因此,渲染對象必須設(shè)為紋理表面。然后我們就可以把東西渲染到這個對象上了。渲染到另一個表面上和正常地渲染到后臺緩沖區(qū)差不多。只有一點不同,那就是先不調(diào)用Prensent()函數(shù),因為紋理上的內(nèi)容并不需要顯示在屏幕上。象平時一樣,我們先要重置表面顏色緩沖區(qū),并且調(diào)用BeginSence()和EndSence()方法。為了能夠適當?shù)匿秩荆覀儽仨氃O(shè)置和紋理表面相符的投影矩陣。否則最后的圖象可能被扭曲
              //render-to-texture
              g_App.GetDevice()->SetRenderTarget(0,pRenderSurface); //set new render target
              g_App.GetDevice()->Clear(0,NULL,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(100,100,100),1.0f,0); //clear texture
              g_App.GetDevice()->BeginScene();
              g_App.GetDevice()->SetTexture(0,pPyramideTexture);
              D3DXMatrixRotationY(&matRotationY,fRotation);
              D3DXMatrixTranslation(&matTranslation,0.0f,0.0f,5.0f);
              g_App.GetDevice()->SetTransform(D3DTS_WORLD,&(matRotationY * matTranslation));
              g_App.GetDevice()->SetTransform(D3DTS_PROJECTION,&matProjection); //set projection matrix
              g_App.GetDevice()->SetStreamSource(0,pTriangleVB,0,sizeof(D3DVERTEX));
              g_App.GetDevice()->DrawPrimitive(D3DPT_TRIANGLELIST,0,4);
              g_App.GetDevice()->EndScene();
              渲染循環(huán)的第二部分就是渲染最后場景的過程(也就是顯示到屏幕上的過程)。渲染對象重新設(shè)為后臺緩沖區(qū),投影矩陣重新設(shè)為原來的投影矩陣。由于紋理已經(jīng)準備好了,所以它和紋理層0相關(guān)聯(lián)。
              //render scene with texture
              g_App.GetDevice()->SetRenderTarget(0,pBackBuffer); //set back buffer
              g_App.GetDevice()->Clear(0,NULL,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(0,0,0),1.0f,0);
              g_App.GetDevice()->BeginScene();
              g_App.GetDevice()->SetTexture(0,pRenderTexture); //set rendered texture
              g_App.GetDevice()->SetTransform(D3DTS_WORLD,&matTranslation);
              g_App.GetDevice()->SetTransform(D3DTS_PROJECTION,&matOldProjection); //restore projection matrix
              g_App.GetDevice()->SetStreamSource(0,pQuadVB,0,sizeof(D3DVERTEX));
              g_App.GetDevice()->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,2);
              g_App.GetDevice()->EndScene();
              g_App.GetDevice()->Present(NULL,NULL,NULL,NULL);
              最后我們通過調(diào)用Release()方法釋放Surface對象。
              pRenderSurface->Release();
              pRenderSurface = NULL;
              pBackBuffer->Release();
              pBackBuffer = NULL;
              渲染到紋理能讓你做很多事情,但是你必須注意一些限制。首先深度緩沖區(qū)必須總是大于或等于渲染對象的大小。此外,渲染對象和深度緩沖區(qū)的格式必須一致。

            posted @ 2009-07-24 23:38 麒麟子 閱讀(802) | 評論 (0)編輯 收藏

            關(guān)于材質(zhì)編輯器的結(jié)構(gòu)

            一直不知道編輯器的結(jié)構(gòu)是如何的。有沒有哪位大大耐心講解一下。或者給一個結(jié)構(gòu)圖也行
            謝謝啦!

            試著用DXUT寫了一下,初步?jīng)Q定以下功能,由于不熟悉DXUT,搗鼓了半天才弄出來

            1、讀取一個模型文件
            2、分析模型文件,取得其subset個數(shù),取得每個subset的材質(zhì)
            3、根據(jù)選中的subset調(diào)整其材質(zhì)
            4、保存編輯好的材質(zhì)到二進制文件中,后綴名暫定為 *.mtrl

            效果圖如下
            至于調(diào)節(jié)材質(zhì)的那里,還有一種方案是調(diào)用shoosecolor面板,那樣比較直觀,但操作復雜。不知道是slider好還是面板好
            暫時用slider


            有誰能告訴我,下拉列表那里,怎么能讓他向上跑嗎? 向下跑會遮住一些,不得不調(diào)整大小。

            posted @ 2009-06-05 13:32 麒麟子 閱讀(1258) | 評論 (2)編輯 收藏

            改進后的 3D貪食蛇

            一開始老是覺得哪里不對勁,后來發(fā)現(xiàn)是自己的光照沒有設(shè)置好。
            如圖的效果就好多了。哈哈

            還是沒有看到哪里加附件,于是我把兩個小游戲的代碼和執(zhí)行文件發(fā)到“文件”里面了,可以去那里下載
            有什么新想法的,大家可以交流。特別是這個貪食蛇的場景。

            posted @ 2009-06-04 11:46 麒麟子 閱讀(1756) | 評論 (6)編輯 收藏

            自己做的小游戲:墻中公主

            手機(聯(lián)想)上有一個游戲叫墻中公主,就是移動光標,如果光標周圍有三個以上顏色相同的方塊。則可以消去。
            玩著玩著就想自己寫一個來試試。當然不是手機版。而是PC版的。游戲很簡單。代碼就不粘上來了。發(fā)張效果圖。呵呵



            我想發(fā)上來呀。但是傳到文件里了,不知道怎么插到這里來。。

            游戲還有一點未完。沒有記分系統(tǒng)。還有就是游戲提示那里少了個   空格:消除方塊

            posted @ 2009-06-01 02:21 麒麟子 閱讀(3945) | 評論 (9)編輯 收藏

            僅列出標題
            共38頁: First 20 21 22 23 24 25 26 27 28 Last 
            国产精品无码久久四虎| 欧美亚洲国产精品久久| 99久久精品日本一区二区免费| 成人妇女免费播放久久久| 精品视频久久久久| 亚洲伊人久久大香线蕉综合图片| WWW婷婷AV久久久影片| 久久久久国产一区二区| 久久综合狠狠综合久久| 人妻中文久久久久| 久久精品视频网| A级毛片无码久久精品免费| 亚洲国产二区三区久久| 亚洲AV无码久久精品狠狠爱浪潮| 久久精品成人免费观看97| 国产亚洲精久久久久久无码| 国产精品久久久久蜜芽| 久久噜噜久久久精品66| 色综合久久久久网| 久久99国产精品久久久| 色综合久久久久无码专区| 亚洲欧美国产日韩综合久久| 国产精品久久久久久福利漫画| 99久久精品免费看国产一区二区三区 | 91精品国产高清久久久久久国产嫩草| 亚洲日韩欧美一区久久久久我| 久久国产乱子精品免费女| 热re99久久精品国99热| 无码国内精品久久人妻| 一本色道久久综合狠狠躁篇 | 久久精品免费一区二区| 中文国产成人精品久久亚洲精品AⅤ无码精品| 久久精品这里热有精品| 久久夜色tv网站| 国产69精品久久久久99| 大蕉久久伊人中文字幕| 国产精品永久久久久久久久久| 亚洲国产精品婷婷久久| 久久久精品久久久久久| 蜜桃麻豆www久久国产精品| 欧美午夜精品久久久久久浪潮|