C++ 中的虚函数和运行时多态| 第 1 套(介绍)

本文介绍了多态性和虚函数的概念,以及它们在继承中的使用。

我们将介绍以下内容

  • 定义
  • 没有虚拟函数
  • 使用虚拟函数

定义:

  1. 函数是在类中使用关键字 virtual 声明并由派生类重新定义(覆盖)的成员函数。
  2. 术语多态性意味着采取多种形式的能力。如果存在通过继承相互关联的类的层次结构,则会发生这种情况。

多态性

类层次结构

注意:在 C++ 中,这意味着如果我们调用成员函数,那么它可能会导致执行不同的函数,这取决于调用它的对象类型。

考虑以下简单程序作为运行时多态性的示例。关于程序的主要注意事项是派生类的函数是使用基类指针调用的。
这个想法是根据指向或引用的对象实例的类型调用虚函数,而不是根据指针或引用的类型。
换句话说,虚函数在运行时被延迟解析。
现在,我们将通过一个使用这两个概念的示例来阐明您的理解。

  • C++
#include <iostream>
using namespace std;

// Base class
class Shape
{
public:
	Shape(int l, int w)
	{
		length = l;
		width = w;
	} // parameterized constructor
	int get_Area()
	{
		cout << "This is call to parent class area\n";
		return 1;
	}

protected:
	int length, width;
};

// Derived class
class Square : public Shape
{
public:
	Square(int l = 0, int w = 0)
		: Shape(l, w)
	{
	} // declaring and initializing derived class
	// constructor
	int get_Area()
	{
		cout << "Square area: " << length * width << '\n';
		return (length * width);
	}
};
// Derived class
class Rectangle : public Shape
{
public:
	Rectangle(int l = 0, int w = 0)
		: Shape(l, w)
	{
	} // declaring and initializing derived class
	// constructor
	int get_Area()
	{
		cout << "Rectangle area: " << length * width << '\n';
		return (length * width);
	}
};

int main()
{
	Shape* s;
	// Making object of child class Square
	Square sq(5, 5);
	// Making object of child class Rectangle
	Rectangle rec(4, 5);

	s = &sq;
	s->get_Area();
	s = &rec;
	s->get_Area();

	return 0;
}

输出

这是对父类区域
的调用 这是对父类区域的调用

在上述函数中:

  • 我们将每个子类RectangleSquare对象的地址存储在s
  • 然后我们在上面调用**get_Area()**函数,
  • 理想情况下,它应该调用子类的相应**get_Area()**函数,但是
  • 相反,它调用基类中定义的get_Area() 。
  • 这是由于静态链接而发生的,这意味着对**get_Area()**的调用仅由基类中的编译器设置一次。

有什么用?
虚函数允许我们创建一个基类指针列表并调用任何派生类的方法,甚至无需知道派生类对象的种类。

例子

考虑一个组织的员工管理软件。
让代码有一个简单的基类Employee,该类包含虚函数,如raiseSalary()transfer()promote()等。不同类型的员工如ManagerEngineer等,可能有自己的虚拟实现基类Employee中存在的函数。
在我们完整的软件中,我们只需要到处传递员工列表并调用适当的函数,甚至不知道员工的类型。例如,我们可以通过遍历员工列表轻松提高所有员工的工资。每种类型的员工在其类中可能有自己的逻辑,但我们不需要担心它们,因为如果*raiseSalary()*存在于特定的员工类型,则只会调用该函数。

  • CPP
class Employee {
public:
	virtual void raiseSalary()
	{
		/* common raise salary code */
	}

	virtual void promote() { /* common promote code */ }
};

class Manager : public Employee {
	virtual void raiseSalary()
	{
		/* Manager specific raise salary code, may contain
		increment of manager specific incentives */
	}

	virtual void promote()
	{
		/* Manager specific promote */
	}
};

// Similarly, there may be other types of employees

// We need a very simple function
// to increment the salary of all employees
// Note that emp[] is an array of pointers
// and actual pointed objects can
// be any type of employees.
// This function should ideally
// be in a class like Organization,
// we have made it global to keep things simple
void globalRaiseSalary(Employee* emp[], int n)
{
	for (int i = 0; i < n; i++) {
		// Polymorphic Call: Calls raiseSalary()
		// according to the actual object, not
		// according to the type of pointer
		emp[i]->raiseSalary();
	}
}

与*globalRaiseSalary()*一样,可以在员工列表上执行许多其他操作,甚至不知道对象实例的类型。
虚函数非常有用,以至于Java 等后来的语言默认将所有方法保留为虚函数。
编译器如何执行运行时解析?
编译器为此目的维护了两件事:

  1. vtable:函数指针表,按类维护。

  2. vptr:指向 vtable 的指针,为每个对象实例维护(请参阅示例)。

编译器执行运行时解析

编译器在两个地方添加了额外的代码来维护和使用vptr
**1)**每个构造函数中的代码。此代码设置正在创建的对象的vptr。此代码将vptr设置为指向该类的vtable
**2)**具有多态函数调用的代码(例如上述代码中的bp->show())。无论在何处进行多态调用,编译器都会插入代码以首先使用基类指针或引用查找vptr(在上面的示例中,由于指向或引用的对象是派生类型,因此访问派生类的vptr)。获取 vptr后,vtable可以访问派生类。使用vtable ,访问和调用派生类函数show()的地址。

这是在 C++ 中实现运行时多态性的标准方法吗?
C++ 标准并没有明确规定必须如何实现运行时多态性,但是编译器通常在相同的基本模型上使用微小的变化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

糖果Autosar

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值