虚函数virtual与多态

本文探讨了C++中虚函数的作用,强调了动态多态性及其通过虚函数实现的方式。讨论了虚函数与引用、指针的多态特性,以及虚函数表的工作原理。此外,还讲解了虚函数与构造、析构、友元的关系,并区分了虚函数的重定义和重载。最后,总结了哪些类型的函数不适合声明为虚函数,包括友元、全局函数、静态成员函数和特定构造函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多态特性详见 C++面向对象的三大特性
动态多态通过虚函数实现。


虚函数virtual

虚函数的用途:

如果希望派生类可以重新定义基类的方法,则可以使用关键字virtual将它声明为虚的。这样对于通过基类指针或引用访问的对象,能够根据对象的类型来调用,而不是根据指针或引用的类型来处理。
示例:
假设要创建一个数组来保存基类对象和派生类对象,但数组中所有元素的类型必须是相同的。可以创建指向基类的指针数组,这样,每个元素的类型都相同,但由于使用的是公有继承类型,所以指针既可以指向基类对象,也可以指向派生类对象。

virtual与引用、指针的多态特性

如果方法是通过引用或指针调用的:

  1. 没有使用关键字virtual:程序将根据引用类型或指针类型选择方法;
  2. 使用了virtual:程序将根据引用或指针指向的对象类型选择方法。
虚函数表

虚函数表是一个数组,表中存储了为类对象进行声明的虚函数的地址
编译器处理虚函数的方法是,为每个对象(如下一段叙述)添加一个隐藏的指针成员,指向虚函数表。
例如,基类对象包含一个指针,指向基类中所有虚函数的地址表;派生类对象也包含一个指向独立地址表的指针。如果派生类提供了虚函数的新定义,该虚函数表将保存新函数的地址,若没有则保存函数原始版本的地址。
调用虚函数时,程序将查看虚函数表中的函数地址,通过地址转向执行相应的函数。

  • 虚函数在内存和执行速度方面的代价:
  1. 因为要存储地址的空间,所以每个对象都将增大;

  2. 对于每个类,编译器都创建一个虚函数地址表(数组);

  3. 对于每个函数调用,都需要执行一项额外操作——到表中查找地址。

    虽然非虚函数效率比虚函数高,但不具备动态联编功能。1

虚函数与构造、析构、友元
  1. 构造函数不能是虚函数:
    编译器在构造对象时,必须知道确切类型才能正确地生成对象;构造函数执行之前,对象并不存在。
    调用派生类的构造函数创建派生类对象时,派生类对象通过初始化成员列表的方式使用基类构造函数,这种方式不同于继承机制。因此,派生类不继承基类的构造函数,将构造函数声明为虚没有任何意义。
  2. 基类的析构函数应当是虚函数:
    为基类声明一个虚析构函数是一种惯例,这样做是为了确保释放派生对象时,按正确的顺序调用析构函数
    当通过指向对象的基类指针或引用来删除派生对象时,如果析构函数不是虚的,则将只调用对应指针类型的析构函数;如果析构函数时虚的,将调用相应对象类型的析构函数。
  3. 友元不能是虚函数:
    友元不是类成员,只有成员才能是虚函数。
虚函数的重新定义和重载的区别

对于如下代码:

class Dewlling
{
public:
	virtual void showperks(int a) const;	//接受一个int参数
...
};
class Hovel : public Dewlling
{
public:
	virtual void showperks() const;	//不接受任何参数
...
};

重新定义不会生成函数的两个重载版本,而是隐藏了基类方法中接受一个int参数的版本。
从中可以看出,如果在派生类中重新定义基类函数,将隐藏所有同名基类方法

  1. 如果重新定义继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针。这种特性被称为返回类型协变。(这种例外只适用于返回值,不适用于参数)
  2. 如果基类声明被重载了,则应在派生类中重新定义所有的基类版本。如果只重新定义一个版本,其他版本将被隐藏,派生类对象将无法使用它们。
总结——一道面试题:那些函数不能定义为虚函数?

经检验下面的几个函数都不能定义为虚函数:
1)友元函数,它不是类的成员函数
2)全局函数
3)静态成员函数,它没有this指针
3)构造函数,拷贝构造函数,以及赋值运算符重载(可以但是一般不建议作为虚函数)


  1. 详见 动态联编和静态联编 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值