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

            山寨:不是最好的,是最適合我們的!歡迎體驗山寨 中文版MSDN

            Blog @ Blog

            當華美的葉片落盡,生命的脈絡才歷歷可見。 -- 聶魯達

            常用鏈接

            統計

            積分與排名

            BBS

            Blog

            Web

            最新評論

            C++中的虛函數(一)

            轉自:http://www.vckbase.com/document/viewdoc/?id=950

            C++中的虛函數(一)


            作者:aber




                雖然很難找到一本不討論多態性的C++書籍或雜志,但是,大多數這類討論使多態性和C++虛函數的使用看起來很難。我打算在這篇文章中通過從幾個方面和結合一些例子使讀者理解在C++中的虛函數實現技術。說明一點,寫這篇文章只是想和大家交流學習經驗因為本人學識淺薄,難免有一些錯誤和不足,希望大家批評和指正,在此深表感謝!

            一、 基本概念
                首先,C++通過虛函數實現多態."無論發送消息的對象屬于什么類,它們均發送具有同一形式的消息,對消息的處理方式可能隨接手消息的對象而變"的處理方式被稱為多態性。"在某個基類上建立起來的類的層次構造中,可以對任何一個派生類的對象中的同名過程進行調用,而被調用的過程提供的處理可以隨其所屬的類而變。"虛函數首先是一種成員函數,它可以在該類的派生類中被重新定義并被賦予另外一種處理功能。

            二、 虛函數的定義與派生類中的重定義

            class 類名{
            public:
            virtual 成員函數說明;
            }
            class 類名:基類名{
            public:
            virtual 成員函數說明;
            }
            
            三、 虛函數在內存中的結構

            1.我們先看一個例子:
            #include "iostream.h"
            #include "string.h"
            class A {
            public:
            virtual void fun0() { cout << "A::fun0" << endl; }
            };
            int main(int argc, char* argv[])
            {
            A  a;
            cout << "Size of A = " << sizeof(a) << endl;
            return 0;
            }      
            結果如下:Size of A = 4

            2.如果再添加一個虛函數:virtual void fun1() { cout << "A::fun" << endl;}
            得到相同的結果。如果去掉函數前面的virtual修飾符
            class A {
            public:
            void fun0() { cout << "A::fun0" << endl; }
            };
            int main(int argc, char* argv[])
            {
            A  a;
            cout << "Size of A = " << sizeof(a) << endl;
            return 0;
            }      
            結果如下:Size of A = 1
             
            3.在看下面的結果:
            class A {
            public:
            virtual void fun0() { cout << "A::fun0" << endl; }
            int a;
            int b;
            };
            int main(int argc, char* argv[])
            {
            A  a;
            cout << "Size of A = " << sizeof(a) << endl;
            return 0;
            }      
            結果如下:Size of A = 12

            其實虛函數在內存中結構是這樣的:


            圖一

                在window2000下指針在內存中占4個字節,虛函數在一個虛函數表(VTABLE)中保存函數地址。在看下面例子。
            class A {
            public:
            virtual void fun0() { cout << "A::fun0" << endl; }
            virtual void fun1() { cout << "A::fun1" << endl; }
            int a;
            int b;
            };
            int main(int argc, char* argv[])
            {
            A  a;
            cout << "Size of A = " << sizeof(a) << endl;
            return 0;
            }
            
            結果如下:結果如下:
            Size of A = 4

                虛函數的內存結構如下,你也可以通過函數指針,先找到虛函數表(VTABLE),然后訪問每個函數地址來驗證這種結構,在國外網站作者是:Zeeshan Amjad寫的"ATL on the Hood中有詳細介紹"


            圖二

            4.我們再來看看繼承中虛函數的內存結構,先看下面的例子
            class A {
            public:
            virtual void f() { }
            };
            class B {
            public:
            virtual void f() { }
            };
            class C {
            public:
            virtual void f() { }
            };
            class Drive : public A, public B, public C {
            };
            int main() {
            Drive d;
            cout << "Size is = " << sizeof(d) << endl;
            return 0;
            }      
            結果如下:Size is = 12 ,相信大家一看下面的結構圖就會很清楚,


            圖三

            5.我們再來看看用虛函數實現多態性,先看個例子:
            class A {
            public:
            virtual void f() { cout << "A::f" << endl; }
            };
            class B :public A{
            public:
            virtual void f() { cout << "B::f" << endl;}
            };
            class C :public A {
            public:
            virtual void f() { cout << "C::f" << endl;}
            };
            class Drive : public C {
            public:
            virtual void f() { cout << "D::f" << endl;}
            };
            int main(int argc, char* argv[])
            {
            A a;
            B b;
            C c;
            Drive d;
            a.f();
            b.f();
            c.f();
            d.f();
            return 0;
            }
            結果:A::f
            B::f
            C::f
            D::f
            
            不用解釋,相信大家一看就明白什么道理!注意:多態不是函數重載

            6.用虛函數實現動態連接在編譯期間,C++編譯器根據程序傳遞給函數的參數或者函數返回類型來決定程序使用那個函數,然后編譯器用正確的的函數替換每次啟動。這種基于編譯器的替換被稱為靜態連接,他們在程序運行之前執行。另一方面,當程序執行多態性時,替換是在程序執行期進行的,這種運行期間替換被稱為動態連接。如下例子:
            class A{
            public:
            virtual void f(){cout << "A::f" << endl;};
            };
            class B:public A{
            public:
            virtual void f(){cout << "B::f" << endl;};
            };
            class C:public A{
            public:
            virtual void f(){cout << "C::f" << endl;};
            };
            void test(A *a){
            a->f();
            };
            int main(int argc, char* argv[])
            {
            B *b=new B;
            C *c=new C;
            char choice;
            do{
            cout<<"type  B for class B,C for class C:"<<endl;
            cin>>choice;
            if(choice==''b'')
            test(b);
            else if(choice==''c'')
            test(c);
            }while(1);
            cout<<endl<<endl;
            return 0;
            }
            
                在上面的例子中,如果把類A,B,C中的virtual修飾符去掉,看看打印的結果,然后再看下面一個例子想想兩者的聯系。如果把B和C中的virtual修飾符去掉,又會怎樣,結果和沒有去掉一樣。

            7.在基類中調用繼承類的函數(如果此函數是虛函數才能如此)還是先看例子:
            class A {
            public:
            virtual void fun() {
            cout << "A::fun" << endl;
            }
            void show() {
            fun();
            }
            };
            class B : public A {
            public:
            virtual void fun() {
            cout << "B::fun" << endl;
            }
            };
            int main() {
            A a;
            a.show();
            return 0;
            }      
            打印結果:A::fun

                在6中的例子中,test(A *a)其實有一個繼承類指針向基類指針隱式轉化的過程。可以看出利用虛函數我們可以在基類調用繼承類函數。但如果不是虛函數,繼承類指針轉化為基類指針后只可以調用基類函數。反之,如果基類指針向繼承類指針轉化的情況怎樣,這只能進行顯示轉化,轉化后的繼承類指針可以調用基類和繼承類指針。如下例子:
            class A {
            public:
            void fun() {
            cout << "A::fun" << endl;
            }
            };
            class B : public A {
            public:
            void fun() {
            cout << "B::fun" << endl;
            }
            void fun0() {
            cout << "B::fun0" << endl;
            }
            };
            int main() {
            A *a=new A;
            B *b=new B;
            A *pa;
            B *pb;
            pb=static_cast<B *>(a); //基類指針向繼承類指針進行顯示轉化
            pb->fun0();
            pb->fun();
            return 0;
            }  
            參考資料:
            1.科學出版社 《C++程序設計》
            2.Zeeshan Amjad 《ATL on the Hood》

            posted on 2008-04-10 17:55 isabc 閱讀(227) 評論(0)  編輯 收藏 引用

            廣告信息(免費廣告聯系)

            中文版MSDN:
            歡迎體驗

            久久久久久久久66精品片| 99久久精品国产一区二区蜜芽 | 久久精品国产亚洲一区二区三区 | 狠狠综合久久综合中文88| 国产69精品久久久久9999| 亚洲性久久久影院| 精品国产91久久久久久久| 久久国产精品视频| 日韩人妻无码一区二区三区久久 | 久久笫一福利免费导航 | 精品久久久久久久久午夜福利| 97久久超碰成人精品网站| 久久一区二区三区99| 精品久久久久久久无码 | 色综合久久无码五十路人妻| 亚洲欧美日韩精品久久| 精品久久久无码人妻中文字幕| 91精品婷婷国产综合久久| 久久久久久久久久久久久久| 26uuu久久五月天| 久久精品国产亚洲AV高清热| 亚洲国产综合久久天堂 | 久久av免费天堂小草播放| 伊人久久大香线蕉综合Av | 一本伊大人香蕉久久网手机| 久久天天躁狠狠躁夜夜躁2014| 91精品国产91久久久久久青草 | 亚洲国产成人精品女人久久久 | 狠狠色狠狠色综合久久| 久久九九免费高清视频| 国产成人精品久久亚洲| 国产福利电影一区二区三区久久久久成人精品综合 | AV色综合久久天堂AV色综合在| 久久人人爽人人爽人人片AV高清 | 色妞色综合久久夜夜| 久久久久波多野结衣高潮| 日韩美女18网站久久精品| 日韩久久久久中文字幕人妻 | 日本免费一区二区久久人人澡| 久久久久久亚洲精品成人| 久久久婷婷五月亚洲97号色|