荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: bakey (大苹果), 信区: Program
标  题: Re: 关于虚函数静态联编与动态联编的问题
发信站: 荔园晨风BBS站 (Thu Jun 29 18:51:46 2006), 站内

网上随便搜到的

作者: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.在基类中调用继承类的函数(如果此函数是虚函数才能如此)还是先看例子: clas
s 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;

}


【 在 bso (屠猪馆) 的大作中提到: 】
: 在某个基类上建立起来的类的层次构造中,可以对任何一个派生类的对象中的同名过程进

: 调用,而被调用的过程提供的处理可以随其所属的类而变。
: ------>>>>这也就是说基类指针可以对派生类中的同名过程(不论是私有或者公有)进行调

: ?
: 刚刚实行了这个规则一下,发现果然如此......呵呵~~~~~~~~这点解释哪里看到的?官方标

: 文档?
: 【 在 bakey (大苹果) 的大作中提到: 】
: : 看一看C++对于虚函数和多态的定义:
: : C++通过虚函数实现多态."无论发送消息的对象属于什么类,它们均发送具有同一形式

: : 消息,对消息的处理方式可能随接手消息的对象而变"的处理方式被称为多态性。"在某
: ...................

--
     心码合一,心中有代码,码中存我心;维码无心,心动即码动,代码表我心;
     维心无码,视万物皆空,知万千变化;无码无心,行如若无规,动全依所意


※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.111.149]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店