现在的位置: 首页 > 综合 > 正文

什么时候应当或不应当使用虚函数

2013年11月09日 ⁄ 综合 ⁄ 共 1485字 ⁄ 字号 评论关闭

本文来自《C++沉思录》阅读笔记。

 

虚函数是实现C++多态性的关键。多态即一个接口多种方法,通过虚函数的动态绑定技术,可以实现C++多态性。

 

多态性可以简单地概括为一个接口,多种方法,程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。多态(polymorphisn),字面意思多种形状。

C++多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写。(这里我觉得要补充,重写的话可以有两种,直接重写成员函数和重写虚函数,只有重写了虚函数的才能算作是体现了C++多态性)而重载则是允许有多个同名的函数,而这些函数的参数列表不同,允许参数个数不同,参数类型不同,或者两者都不同。编译器会根据这些函数的不同列表,将同名的函数的名称做修饰,从而生成一些不同名称的预处理函数,来实现同名函数调用时的重载问题。但这并没有体现多态性。

多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。

那么多态的作用是什么呢,封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。而多态的目的则是为了接口重用。也就是说,不论传递过来的究竟是那个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。

 

最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因为没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用到同一个函数,这就无法实现一个接口,多种方法的目的了。

 

虚函数用法:

适用的情况:如果只关注程序的行为,同事没有继承关系,那么函数是否为虚函数根本无关紧要,只有当涉及到继承的时候,才有必要考虑一些问题。

 

不适应的情况:

l  虚函数代价不是很昂贵,可是也存在开销,要考虑效率

l  有些情况下非虚函数能正确运行,而虚函数却不行

l  不是所有类都是为了继承而设计的

 

如果打算让你的类支持继承,那么即使你不是使用其他虚函数,可能还是需要一个虚析构函数。

只有当使用一个基类指针或引用类指向或者引用一个派生类对象时,虚函数和非虚函数之间才有区别。

 

需要虚析构函数的情况:

l  有需要析构函数的事情发生。

l  它发生在这样一种上下文中:指向一个基类的指针或者引用都有一个静态类型,并且实际上都指向一个派生类的对象。

因此,只有当使用指向基类的指针来删除派生类的对象时,虚析构函数才有真正的意义。

如果需要一个虚析构函数,定义一个空的虚析构函数就行了。

 

总结为什么虚析构函数不总是适用,有3个原因:其一是虚函数有时会带来很大的开销,其二是虚函数不总是提供所需的行为,其三是有时我们写一个类时,可能不想考虑派生问题。

另外,有一种情况必须适用虚函数:当你想删除一个表面上指向基类对象、实际却是指向派生类对象的指针,就需要虚析构函数。

 

需要注意的是:只有通过引用或指针调用,虚函数才在运行时确定,只有在这些情况下,直到运行时才知道对象的动态类型。

静态类型:static type,在编译时可知的引用类型或者指针类型。

动态类型:dynamic type,指针或者引用多绑定的对象类型,这是仅在运行时可知。

 

抱歉!评论已关闭.