多态性与虚函数

一. 重载函数
1. 在C++语言中,编译程序选择相应的重载函数版本时函数返回值类型是不起作用的。不能仅靠函数的返回值来区别重载函数,必须从形式参数上区别开来。例如:
void print(int a);
void print(int a,int b);
int print(float a[]);
这三个函数是重载函数,因为C++编译程序可以从形式参数上将它们区别开来。

但是:
int f(int a);
double f(int a);
不能进行重载,会发生编译错误。

2. 函数重载的二义性(ambiguity):
  (1). 是指C++语言的编译程序无法在多个重载函数中选择正确的函数进行调用。这些二义性错误是致命的,因而编译程序将无法生成目标代码。
函数重载的二义性主要源于C++语言的隐式类型转换与默认参数.


  (2). 在重载函数中使用默认参数也可能造成二义性

3. 析构函数不能进行重载,因为析构函数本来就是无参数的,无返回类型的。

/* 例1:test1.cpp   */
/* IDE环境: Dev-C++ 4.9.9.2 */
/* 重载函数举例 */

#include <stdio.h>
class base
{
    public:
           //构造函数重载
           base():i(0){ };
           base(int x) { i = 10; }
           //base (int y){ j = 20; } // compile: F:/devcpptest/devcpptest/test1.cpp `base::base(int)' and `base::base(int)' cannot be overloaded
                                       //形式与上一个一样。
           base(int x,int y) { i = x; j = y; }
          
          
           int get_i(){ return i; }
          
           //其它函数重载:
           void  display(){ printf(" base::display_i, i = %d, j = %d /n", i, j); } 
           void  display(int type)
           {
                 if (type)
                    printf(" base::display, i = %d/n", i);
                 else
                     printf(" base::display, j = %d /n", j);
           }
          
           float abs(float x)
           {
               return (x>0?x:-x);
           }
           double abs(double x)
           {
               return (x>0?x:-x);
           }

           //析构函数不能重载:
           ~base(){};
           //~base(int type) { i = type; } // F:/devcpptest/devcpptest/test1.cpp `base::~base()' and `base::~base()' cannot be overloaded
          
    private:
            int i;
            int j;
};


int main()
{
    base b;
    printf("abs(1.78) = %f /n", b.abs(1.78)) ; // OK, 调用abs(double)
    //printf("abs(-7) = %f /n", b.abs(-7));    // error,编译程序无法确定调用哪一个abs()函数

    while(1);
}

二. 虚函数
1. 静态联编与动态联编
   (1)静态联编:是指联编工作出现在编译连接阶段,这种联编过程是在程序开始运行之前完成的。在编译时所进行的这种联编又称静态绑定。
        在编译时就解决了程序中的操作调用与执行该操作代码间的关系,确定这种关系又称为绑定,在编译时绑定又称静态聚束。
   (2)动态联编:编译程序在编译阶段并不能确切知道将要调用的函数,只有在程序执行时才能确定将要调用的函数,
        为此要确切知道该调用的函数,要求联编工作要在程序运行时进行,这种在程序运行时进行的联编工作被称为动态联编,或称动态绑定。
        动态联编实际上是进行动态识别。
   (3)比较:

程序中的形式
 聚束方式
 函数原型
 决策依据
 特点
 
重载函数
 静态聚束
 不同
 参数数目及类型
 高效
 
虚函数
 动态聚束
 相同
 运行时指针的指向或引用
 高效灵活,抽象性,可扩充性
 


 


 
  
2. 虚函数的概念
   虚函数是在基类中冠以关键字 virtual 的成员函数。它是动态联编的基础。虚函数是成员函数,而且是非static的成员函数。
   它提供了一种接口界面,并且可以在一个或多个派生类中被重定义。
   一个虚函数是属于它所在的类层次结构的,而不是只属于某一个类。
3. 动态联编与虚函数
   派生类中对基类的虚函数进行替换时,要求派生类中说明的虚函数与基类中的被替换的虚函数之间满足如下条件:
(1)与基类的虚函数有相同的参数个数;
(2)其参数的类型与基类的虚函数的对应参数类型相同;
(3)其返回值或者与基类虚函数的相同,或者都返回指针或引用,
     并且派生类虚函数所返回的指针或引用的基类型是基类中被替换的虚函数所返回的指针或引用的基类型的子类型。
(4)在派生类对基类中声明为虚函数进行重新定义时,关键字virtual也可以不用写,即 对于
    class Point {
    private:
        flat x,y;
    public:
        virtual float area() // 虚函数
        {
            return 0.0;
        }/
    };
    const float PI = 3.141593;
    class Circle: public Point {
    private:
        float radius;
    public:
        virtual float area()
        {
            return PI*radius*radius;
        }
    };
    派生类类中的virtual float area() 也可以写成下面的形式:
    float area()。

4. 虚函数的限制

(1)在类体系中访问一个虚函数时,应使用指向基类类型的指针或对基类类型的引用,以满足运行时多态性的要求。
(2)在派生类中重新定义虚函数时,必须保证该函数的值和参数与基类中的说明完全一致,否则就属于重载(参数不同)或是一个错误(返回值不同)。
(3)若在派生类中没有重新定义虚函数,则该类的对象将使用其基类中的虚函数代码。
(4)虚函数必须是类的一个成员函数,不能是友元,但它可以是另一个类的友元。
(5)析构函数可以是virtual的虚函数,但构造函数则不得是虚函数。
(6)一个类的虚函数仅对派生类中重定义的函数起作用,对其他函数没有影响。

5. 虚函数与重载函数的比较
(1)重载函数要求函数有相同的返回值类型和函数名称,并有不同的参数序列;而虚函数则要求这三项(函数名、返回值类型和参数序列)完全相同。
(2)重载函数可以是成员函数或友元函数,而虚函数只能是成员函数。
(3)重载函数的调用是以所传递参数序列的差别作为调用不同函数的依据;虚函数是根据对象的不同去调用不同类的虚函数。
(4)虚函数在运行时表现出多态功能,这是C++的精髓;而重载函数则在编译时表现出多态性。

6. 虚函数的访问:
(1)用基指针访问与用对象名访问;
     虚函数被指向基类的指针(或引用)调用时,C++对其进行动态聚束,向实际的对象传递消息。
     但是,通过一个对象名访问虚函数时,C++系统将采用静态聚束。
     例如,对于上面的Point 和 Circle 类,
     Point *pp;
     Circle c;
     pp = &c;
     pp->area(); //调用的是Circle的area
     调用的是Circle的area;
     而,
     Circle c;
     c.area();            // 调用的是本类Circle的area。
     c.Point::area();     // 调用的是父类Point的area,采用作用域运算符来指定调用父类的方法。
     c.Circle::area();    // 调用的是本类Circle的area。
    
    

(2)由类的成员函数访问该类层次中的的虚函数,要使用this指针;例如,
      #include <stdio.h>
     
      class A {
            public:
                   virtual void vir_func1() {
                       printf(" v1 is called in A./n");
                       a1();
                   }
                   virtual void vir_func2() {
                       printf(" v2 is called in A./n");
                   }
                   void a1() {
                       printf(" a1 is called in A./n");
                       vir_func2();  // 等价于 this->vir_func2()
                   }
      };
     
      class B: public A {
            public:
                  
                   virtual void vir_func2() {
                       printf(" v2 is called in B./n");
                   }
                   void b1() {
                       printf(" b1 is called in B./n");
                       vir_func2();
                   }
      };
      int main()
      {
          A a;
          a.vir_func1();
          printf(" OK./n");
         
          B b;
          b.vir_func1();
         
          while(1);
          return 0;
      }
      /*
      result:
            v1 is called in A.
            a1 is called in A.
            v2 is called in A.
            OK.
            v1 is called in A.
            a1 is called in A.
            v2 is called in B.
           
      */

    
(3)用构造函数和析构函数访问:
     这两个函数访问虚函数时,C++采用静态聚束。例如,
      #include <stdio.h>
     
      class A {
            public:
                   A() { };
                   virtual void vir_func1() {
                       printf(" v1 is called in A./n");
                   }
      };
     
      class B: public A {
            public:
                   B() {
                       printf(" call v1 in B./n");
                       vir_func1(); //  调用本类中定义的虚函数
                   }
                   virtual void vir_func1() {
                       printf(" v1 is called in B./n");
                   }
      };
     
      class C: public B {
            public:
                   C() {
                       printf(" call v1 in C./n");
                       A::vir_func1();  // 调用基类中定义的虚函数
                   }
                   void vir_func1() {
                       printf(" v1 is called in C./n");
                   }
      };
      int main()
      {
          B b;
          printf(" OK./n");
         
          C c;
        
          while(1);
          return 0;
      }
      /*
      result:
            call v1 in B.
            v1 is called in B.
            OK.
            call v1 in B.
            v1 is called in B.
            call v1 in C.
            v1 is called in A.
           
      */

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/liranke/archive/2009/11/12/4800830.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值