静态联编和动态联编虚函数

一、静态联编与动态联编
将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编(binding)
在c++中由于函数重载使得编译器必须查看函数参数以及函数名才能确定使用哪个函数。
在编译过程中进行联编被称为静态联编(static binding)又称为早期联编(early binding)

又因为虚函数的存在,使得程序使用哪个函数是不能在编译时确定的,因为编译器不知道用户将选择哪种类型的对象。所以编译器必须生成能够在程序运行时选择正确的虚函数方法的代码,这种方法称为动态联编(dynamic binding)又称为晚期联编(late binding ).

二、虚函数
在《C++Primer Plus》第13章类继承中有过详细的说明。通常,编译器处理虚函数的方法是:给每一个对象添加一个隐藏成员。隐藏成员中保存了一个只想函数地址数组的指针。这种数组成为虚函数表(vtable)。虚函数表中存储了为类对象进行声明的虚函数的地址。例如基类对象包含一个指针,则该指针指向的是基类中所有虚函数的地址表。
派生类对象将包含一个指向独立地址表的指针。如果派生类为虚函数提供了新的定义,则该虚函数表保存的是新函数的地址。
有关虚函数的注意事项:

  1. 在基类方法的声明中使用关键字virtual可使该方法在基类以及所有派生类(以及从派生类中派生出来的类)都是虚的。

  2. 如果使用指向对象的引用或指针来调用虚方法,程序将使用为对象类型定义的方法。而不使用为引用指针类型定义的方法。这称为动态联编或晚期联编。这样的话基类指针或引用可以指向派生类对象。

  3. 如果定义的类被用作基类。则应将那些要在派生类中重新定义的类方法声明为虚的。

    什么函数可以是虚函数,什么不可以是:

不可以是虚函数的


(1)构造函数不能是虚函数
创建派生类对象时,将调用派生类的构造函数而不是基类的构造函数,然后派生类的构造函数将使用基类的一个构造函数,这种顺序不同于继承机制,换句话说,派生类不继承基类的构造函数,因此不需要把基类的构造函数设置成虚的。
(2)友元函数不能是虚函数
因为友元不是类成员,只有成员才能是虚函数。但是可以让友元函数使用虚成员函数。

可以是虚函数的:

1、析构函数
析构函数应该是虚函数,除非不用做基类。
下面看一个代码:

 Employee *pe =new Singer;//Employee是基类,Singer是派生类
 ....
 delete pe;   

如果使用的是默认的静态联编,delete语句将调用~Employee()析构函数。这里释放的是Singer对象中Employee部分指向的内存,但不会释放新的类成员指向的内存。
如果析构函数是虚的呢?
那么上述代码将先调用的是~Singer析构函数,释放的是Singer的组件指向的内存,然后再调用~Employee()析构函数来释放由Employee指向的内存。
4、没有重新定义
如果派生类没有重新定义函数,将使用该函数的基类版本,如果派生类位于派生链中,将使用最新的虚函数版本。
5、重新定义的话将隐藏方法

class Dwelling{
public:
      virtual void showperks(int a)const;

}
class Hovel:public Dwelling
{
public:
     virtual void showperks()const;
}

virtual情况下:在派生类中新定义的showperks()定义为一个不接受任何参数的函数。这种情况重新定义不会生成函数的两个重载版本,而是隐藏接受一个int参数的基类版本。换句话就是重新定义的继承的方法不是重载。如果重新定义派生类中的函数,不只是使用相同的函数参数列表覆盖基类声明,无论参数列表是否相同,该操作都会隐藏所有的同名基类方法。
也就是执行的时候只会执行showperks()const;
代码执行情况:

Hovel trump;
trump.showperks();//有效的
trump.showperks(5);//无效的,因为被隐藏了

对于这种问题,引出两条经验:
一、如果重新定义继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针。这种特性被称为返回类型协变。允许返回类型随类类型的变化而变化:

class Dwelling
{
public:
      virtual Dwelling & build(int n);
};
class Hovel:public Dwelling
{
public:
      virtual Hovel & bulid(int n);
}

二、
如果基类声明被重载了,则应在派生类中重新定义所有的基类版本

class Dwelling
{
public:
//被重载的showperks()
      virtual void showperks();
      virtual void showperks(int a)const;
      virtual void showperks(double x)const;
      virtual void showperks()const;

};
class Hovel:public Dwelling
{
public:
//三个重新定义的showperks()
      virtual void showperks();
      virtual void showperks(int a)const;
      virtual void showperks(double x)const;
      virtual void showperks()const;
}

如果只重新定义一个版本,则另外两个版本将被隐藏,派生类对象都无法使用它们,如果不需要修改,则新定义可只调用基类版本:

void Hovel ::showperks()const
{
  Dwelling ::showperks();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值