本文介绍了多态性和虚函数的概念,以及它们在继承中的使用。
我们将介绍以下内容
- 定义
- 没有虚拟功能
- 使用虚函数
定义:
- 虚函数是成员函数,它在基类中使用关键字virtual声明,并由派生类重新定义(重写)。
- 术语“多态”是指采取多种形式的能力。如果存在通过继承相互关联的类的层次结构,则会发生这种情况。
Note: In C++ what this means is that if we call a member function then it could cause a different function to be executed instead depending on what type of object invoked it.
考虑以下简单程序作为运行时多态性的示例。有关该程序的主要注意事项是,使用基类指针调用派生类的函数。
想法是,虚函数是根据指向或引用的对象实例的类型而不是根据指针或引用的类型来调用的。
换句话说,虚拟函数在运行时解析得较晚。
现在,我们来看一个使用这两个概念的示例,以阐明您的理解。
C++
#include
using namespace std;
// Base class
class Shape
{
public:
Shape(int l, int w)
{
length = l;
width = w;
} // default constructor
int get_Area()
{
cout << "This is call to parent class area" << endl;
}
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 << endl;
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
<< endl;
return (length * width);
}
};
int main(void)
{
Shape* s;
Square sq(5, 5); // making object of child class Sqaure
Rectangle rec(
4, 5); // making object of child class Rectangle
s = &sq;
s->get_Area();
s = &rec;
s->get_Area();
return 0;
}
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();
}
This is call to parent class area
This is call to parent class area
在以上函数:
- 我们将每个子类Rectangle和Square对象的地址存储在s和
- 然后我们在其上调用get_Area()函数,
- 理想情况下,它应该已经调用了子类的相应get_Area()函数,但是
- 而是调用基类中定义的get_Area() 。
- 这是由于静态链接而发生的,这意味着对get_Area()的调用仅由基类中的编译器设置一次。
有什么用?
虚函数使我们可以创建基类指针列表和任何派生类的调用方法,甚至无需知道派生类对象的种类。
例如:考虑组织的员工管理软件。
让代码具有一个简单的基类Employee ,该类包含虚拟函数,例如raiseSalary() , transfer() , promote()等。不同类型的雇员,例如Manager , Engineer等,可能具有自己的虚拟函数实现存在于基类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之类的较后语言在默认情况下会将所有方法保持为虚方法。
编译器如何执行运行时解析?
编译器维护两件事来实现此目的:
- vtable:每个类维护的函数指针表。
- vptr:指向vtable的指针,针对每个对象实例进行维护(请参见示例)。