如何在 c++ 中决定虚拟表中的索引
How indexing within the virtual table is decided in c++?
请考虑以下代码。
#include<iostream>
using namespace std;
class Base
{
public:
virtual void function1() {cout<<"Base:function1()n";};
virtual void function2() {cout<<"Base:function1()n";};
};
class D1: public Base
{
public:
virtual void function1() {cout<<"D1:function1()n";};
virtual void function2() {cout<<"D1:function2()n";};
};
int main()
{
Base *ptr= new D1;
ptr->function1();
ptr->function2();
return 0;
}
ptr 将指向 D1 obj。因此,每当我调用 ptr->function1() 时,函数地址都会从类 D1 的虚拟表中获取。它对ptr->function2()的工作方式相同。
在这种情况下,vtable[0] 将具有指向 function1() 的函数指针,vtable[1] 将具有指向 function2() 的函数指针。
我的问题是对 vtable 索引映射的函数调用是如何发生的?
ptr->function1() 和 ptr->function2() 如何分别索引到 vtable[0] 和 vtable[1]?
类的第一个元素通常是(隐藏的)vtable指针 - vptr。对于多态类,vtable 首先初始化为基类构造函数中基类的 vtable。然后,当派生类构造函数执行相同的 vtable 指针时,将初始化相同的 vtable 指针以指向派生类 vtable。请注意,基类 vtable 指向函数 1 和函数 2 的基版本,而派生类 vtable 指向函数 1 和函数 2 的派生版本。现在,当指向基类的指针指向派生类的实例时,这就是"通常可能"发生的情况:
class base
{
//int* vptr; //hidden vtable pointer, created by compiler for polymorphic class. vptr points to base class vtable for base clas objects
public:
virtual void function1(){std::cout <<"base::function1()"<<std::endl;}
virtual void function2(){std::cout <<"base::function2()"<<std::endl;}
};
class derived: public base
{
//int* vptr; //hidden vtable pointer, inherited from the base class. vptr points to derived class vtable for derived class objects
public:
virtual void function1(){std::cout <<"derived::function1()"<<std::endl;}
virtual void function2(){std::cout <<"derived::function2()"<<std::endl;}
};
int main()
{
typedef void (*vtableFnPtr)();
base* pBase;
base base_obj;
derived derived_obj;
pBase = &derived_obj; //base pointer pointing to derived object
//one of the several possible implementations by compiler
int* vtableCallBack = *(int**)&derived_obj; //read the address of vtable pointed by the hidden vptr in the derived_obj
//pBase->function1();
((vtableFnPtr)vtableCallBack[0])(); //calls derived::function1(), when application calls pBase->function1();
//pBase->function2();
((vtableFnPtr)vtableCallBack[1])(); //calls derived::function2(), when application calls pBase->function2();
pBase = &base_obj;
//one of the several possible implementations by compiler
vtableCallBack = *(int**)&base_obj; //base pointer pointing to base object
//pBase->function1();
((vtableFnPtr)vtableCallBack[0])(); //calls base::function1(), when application calls pBase->function1();
//pBase->function2();
((vtableFnPtr)vtableCallBack[1])(); //calls base::function2(), when application calls pBase->function2();
}
请注意,C++编译器没有说明用于实现多态行为的实现方法,因此,只要行为是多态的,编译器就可以使用 vtable 或任何其他实现。然而,vtable 仍然是实现多态行为的最广泛使用的方法之一。
"计算机科学中的所有问题都可以通过另一个层次的间接解决"
我提到了优秀而详细的博客文章,并在下面尝试为您的简单示例解释 vtable 的使用。看看你是否读透了。
struct Base;
// enumerates all virtual functions of A
struct table_Base {
void (*function1)(struct Base *this);
void (*function2)(struct Base *this);
};
struct Base {
const struct table_Base *pvtable; // table maintains pointers to virtual functions. Eventually to implementations
int data;
};
void Base_function1(struct Base *this) {
std::cout << "Base:function1()" << std::endl;
}
void Base_function2(struct Base *this) {
std::cout << "Base:function2()" << std::endl;
}
// table data for Base
static const struct table_Base table_Base_for_Base = { Base_function1, Base_function2};
void Base_Ctor(struct Base *this) {
this->pvtable = &table_Base_for_Base;
this->data = 1;
}
// Now for class D1
struct D1;
struct table_D1 {
void (*function1)(struct D1 *this);
void (*function2)(struct D1 *this);
};
struct D1 {
struct Base base;
const struct table_D1 *pvtable;
int more_data;
};
void D1_function1(struct D1 *this) {
std::cout << "D1:function1()" << std::endl;
}
void D1_function2(struct D1 *this) {
std::cout << "D1:function2()" << std::endl;
}
// Important functions that do re-direction
void D1Base_function1(struct Base *this) {
D1_function1((struct D1*) this);
}
void D1Base_function2(struct Base *this) {
D1_function2((struct D1*) this);
}
// table data for D1
static const struct table_D1 table_D1_for_D1 = {D1_function1, D1_function2};
// IMPORTANT table
static const struct table_Base table_Base_for_D1 = {D1Base_function1, D1Base_function2};
// Constructor for derived class D1
void D1_Ctor(struct D1 *this)
{
Base_Ctor(&this->base); // Base class vtable is initialized.
// Now, Override virtual function pointers
this->base.vtbl = &table_Base_for_D1; // Replace the vtable
this->mode_data = 100;
}
在内部,这是编译器为虚拟函数实现正确行为所遵循的逻辑。
ptr->function1();
ptr->function2();
您可以使用解释的 vtable 逻辑跟踪上面的方法调用,看看它是否有效。
- 数组索引的值没有增加
- 芬威克树(BIT).找到具有给定累积频率的最小索引,单位为 O(logN)
- 虚拟决赛作为安全
- PowerPC ppc64le上的Gcc Woverloaded虚拟错误
- 查找最接近的大于当前数字的数字的索引
- 在C++中调整向量中的索引
- 如何在C++中获得"静态纯虚拟"功能?
- C++无法定义虚拟函数 OUTER 类和头文件
- 重载元组索引运算符-C++
- 给定一个向量,如何找到该向量的所有子集和的原始索引
- 为std::string的某个索引赋值
- 并行用于C++17中数组索引范围内的循环
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 尝试将unique_ptrs推送到向量时使用纯虚拟函数错误
- 跟随整数索引列表的自定义类迭代器
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 如何在for循环中包含两个索引值的测试条件
- 模型中虚拟索引实现的替代方法
- 如何在 c++ 中决定虚拟表中的索引