Chinaunix首页 | 论坛 | 博客
  • 博客访问: 108425
  • 博文数量: 40
  • 博客积分: 1650
  • 博客等级: 上尉
  • 技术积分: 420
  • 用 户 组: 普通用户
  • 注册时间: 2007-07-20 13:05
文章分类
文章存档

2011年(1)

2009年(1)

2008年(1)

2007年(37)

我的朋友

分类: C/C++

2007-07-25 09:35:15

1. 虚函数基础
    虚函数通俗地讲就是那些被virtual关键字修饰的成员函数。
    虚函数的作用是用来实现多态性(Polymorphism)。
    多态性是将接口与实现进行分离。
    指向基类的指针和引用在操作它的派生类对象时,会根据不同的类对象,调用对应的函数。
    虚函数的定义在基类中进行,在需要定义为虚函数的成员函数的声明前冠以关键字 virtual。
    基类中的某个成员函数被声明为虚函数后,此虚函数就可以在一个或多个派生类中被重新定义。
    在派生类中重新定义时,其函数原型,包括返回类型,函数名,参数个数,参数类型及参数的先后顺序,都必须与基类中的原型完全相同。
    虚函数是重载的一种表现形式,是一种动态重载方式。
2. 为什么使用虚函数
    #include
    using namespace std;

    class Base
    {
      public:
      void out ()
       {
          cout << "base class!" << endl;
       }
    };
    class Derive1:public Base
    {
      public:
      void out ()
        {
          cout << "derive1 class!" << endl;
        }
    };
    class Derive2:public Base
    {
       public:
       void out ()
        {
          cout << "derive2 class!" << endl;
        }
    };
    int
    main ()
    {
        Derive1 object1;
        Derive2 object2;
        Base object3;
        Base *object = &object1;
        object->out ();
        object = &object2;
        object->out ();
        object = &object3;
        object->out ();
        object1.out ();
        object2.out ();
        object3.out ();
        return 0;
    }

    运行结果:
    base class!
    base class!
    base class!
    derive1 class!
    derive2 class!
    base class!
    通过对象指针进行的普通成员函数调用,仅仅与指针的类型有关,而与此刻正指向什么对象无关。
    #include
    using namespace std;

    class Base
    {
      public:
      virtual void out ()
        {
          cout << "base class!" << endl;
        }
    };
    class Derive1:public Base
    {
       public:
       void out ()
        {
          cout << "derive1 class!" << endl;
        }
    };
    class Derive2:public Base
    {
       public:
       void out ()
        {
          cout << "derive2 class!" << endl;
        }
    };
    int
    main ()
    {
        Derive1 object1;
        Derive2 object2;
        Base object3;
        Base *object = &object1;
        object->out ();
        object = &object2;
        object->out ();
        object = &object3;
        object->out ();
        object1.out ();
        object2.out ();
        object3.out ();
        return 0;
    }
    运行结果:
    derive1 class!
    derive2 class!
    base class!
    derive1 class!
    derive2 class!
    base class!
    可见当将基类中对应的成员函数定义为虚函数,实现了当指针指向不同对象时执行不同的策略。
3. 虚函数是如何做到因对象的不同而调用其相应的函数的

    非虚成员函数是静态确定的。即,非虚成员成员函数(在编译时)被静态地选择,该选择基于指向对象的指针(或引用)的类型。

    虚成员函数是动态确定的(在运行时),也就是说,成员函数(在运行时)被动态地选择,该选择基于对象的类型,而不是指向该对象的指针/引用的类型。这被称作“动态绑定”。大多数的编译器使用以下技术来实现:如果对象有一个或多个虚函数,编译器将一个隐藏的指针放入对象,该指针称为“virtual-pointor”或“v-pointer”。这个v-pointer指向一个全局表,该表称为“虚函数表(virtural-table)”或“v-table”。

    编译器为每个含有至少一个虚函数的类创建一个v-table。例如,如果Base类有虚函数out()、in() 和 save(),那么将有且只有一个和Base类相关的v-table,即使有一大堆Base对象。并且每个Base对象的 v-poiner将指向 Base的这个 v-table。该 v-table自己有指向类的各个虚函数的指针。例如,Circle 的v-table 会有三个指针:一个指向Circle::out(),一个指向 Circle::in(),一个指向Circle::save()。

    在调用一个虚函数时,系统通过对象的 v-pointer找到类的 v-table,然后通过查找v-table得到方法的地址。

    上述技术会导致空间开销和时间开销:
    每个对象一个额外的指针(仅仅对于需要动态绑定的对象),加上每个方法一个额外的指针(仅仅对于虚方法),这会增加空间开销。
    和普通函数调用比较,虚函数调用需要两个额外的步骤(得到v-pointer的值,得到方法的地址)。
    这些开销不会发生在非虚函数上。
4. 虚函数与重载函数的关系
    一般的重载函数,函数的返回类型及所带的参数必须至少有一样不完全相同,只需函数名相同即可。
    基类中定义的虚函数在派生类中重新定义时,其函数原型,包括返回类型,函数名,参数个数,参数类型及参数的先后顺序,都必须与基类中的原型完全相同。
    重载虚函数时,若与基类中的函数原型出现不同,系统将根据不同情况分别处理:
    (1)仅仅返回类型不同,其余相同,系统会当作出错处理;
    (2)函数原型不同,仅仅函数名相同,系统会认为是一般的函数重载,将丢失虚特性。


阅读(811) | 评论(2) | 转发(0) |
0

上一篇:系统V共享内存原理

下一篇:虚基类简介

给主人留下些什么吧!~~