C++虚函数表的原理与使用解析
简介
在C++的类继承中,为了实现多态,我们常常会使用虚函数。虚函数与虚函数表有着密切的关系,在本文中,我们将深入探讨C++虚函数表的原理和使用。
虚函数的概念
虚函数是在C++中用于实现多态的重要机制。通过在基类中声明虚函数,在子类中覆盖该虚函数,可以在运行时根据对象的实际类型来调用相应的函数实现,从而实现多态。
虚函数的声明方式如下:
class Base {
public:
virtual void func();
};
其中,关键字virtual
表示这是一个虚函数。在子类中覆盖该虚函数时,需要使用override
关键字:
class Sub : public Base {
public:
void func() override;
};
虚函数表的原理
虚函数表(Virtual Table,简称VTable)是C++实现多态机制的重要机制之一。在C++中,每一个带有虚函数的类都会有一个对应的虚函数表,虚函数表里存放着该类所有虚函数的地址。
当调用一个对象的虚函数时,编译器会查询该对象所属的类的虚函数表,找到对应的函数地址并调用之。
在C++中,每一个带有虚函数的类都会被分配一个虚函数表,在其中存储该类所有虚函数的信息。虚函数表一般包括两个元素:一个指向虚函数表本身的指针(vptr),以及该类所有虚函数的地址(vfunc)。vptr的值是在对象创建时被初始化的,指向该对象的虚函数表。
虚函数表的使用
下面,我们来看一个使用虚函数表的例子。假设我们要实现一个图形类,可以绘制不同类型的图形。我们可以定义一个基类Shape,声明一个虚函数draw(),用于绘制图形。同时,派生出不同类型的子类Circle、Square等,重写draw()函数。
class Shape {
public:
virtual void draw() = 0;
};
class Circle : public Shape {
public:
void draw() override {
cout << "绘制一个圆形" << endl;
}
};
class Square : public Shape {
public:
void draw() override {
cout << "绘制一个正方形" << endl;
}
};
可以看到,在基类Shape中声明了一个纯虚函数draw()
,并在派生类Circle和Square中实现了该函数。现在,我们可以创建不同类型的图形对象,并使用它们的draw()
函数进行绘制。
int main() {
Circle c;
Square s;
Shape *pShape = &c;
pShape->draw();
pShape = &s;
pShape->draw();
return 0;
}
在这个例子中,首先我们定义了一个Circle对象和一个Square对象,然后用Base类指针pShape
依次指向两个对象,并调用它们的draw()
函数。程序将会输出:
绘制一个圆形
绘制一个正方形
这是因为,在draw()
函数被调用时,编译器会根据pShape
指针所指对象的类型,找到相应的虚函数地址并调用之,从而实现多态。
示例2
来看一个稍微复杂一些的例子。假设我们有一个基类Animal,派生出类Dog和类Cat。除了虚函数func()外,派生类还定义了其他类型的函数,如Dog::bark()和Cat::meow()。我们可以通过调用派生类的非虚函数来验证虚函数表的使用:
#include <iostream>
using namespace std;
class Animal {
public:
virtual void func() {
cout << "Animal func()" << endl;
}
void sleep() {
cout << "Animal sleep()" << endl;
}
};
class Dog : public Animal {
public:
void func() override {
cout << "Dog func()" << endl;
}
void bark() {
cout << "Dog bark()" << endl;
}
};
class Cat : public Animal {
public:
void func() override {
cout << "Cat func()" << endl;
}
void meow() {
cout << "Cat meow()" << endl;
}
};
int main() {
Animal *pAnimal = new Dog;
pAnimal->func();
pAnimal->sleep();
//pAnimal->bark();//编译出错
delete pAnimal;
pAnimal = new Cat;
pAnimal->func();
pAnimal->sleep();
//pAnimal->meow();//编译出错
delete pAnimal;
return 0;
}
在这个例子中,我们首先定义了一个Animal类,并在其中声明了虚函数func()
和普通函数sleep()
。接着,我们派生出了Dog类和Cat类,并分别重写了func()
函数并增加了各自的其他函数。
在main()函数中,我们创建了一个指向Dog对象的Animal指针pAnimal,并调用了两个函数:func()
和sleep()
。输出结果为:
Dog func()
Animal sleep()
可以看到,虽然pAnimal指向的是Dog对象,但是调用func()
函数时并没有调用基类的实现,而是调用了Dog类中的实现,这就是虚函数多态的实现。
至于为什么不能调用bark()
函数,那是因为该函数是派生类Dog的非虚函数,在编译时会被认为是Animal类的函数。因此,如果我们尝试通过Animal指针调用该函数,就会编译出错。同样地,尝试通过Animal指针调用Cat类的函数meow()
也会失败。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++虚函数表的原理与使用解析 - Python技术站