代码实现
#include <iostream>
class BaseClass {
public:
virtual void virtualFunction() {
std::cout << "BaseClass::virtualFunction" << std::endl;
}
};
// 公有继承的TemplateClass
template <typename T>
class TemplateClassPublic : public BaseClass {
public:
void someFunction() {
// 类内操作
}
};
// 私有继承的TemplateClass
template <typename T>
class TemplateClassPrivate : private BaseClass {
public:
void someFunction() {
// 类内操作
}
// 提供公有接口调用虚函数
void callVirtualFunction() {
BaseClass::virtualFunction();
}
};
// 保护继承的TemplateClass
template <typename T>
class TemplateClassProtected : protected BaseClass {
public:
void someFunction() {
// 类内操作
}
// 提供公有接口调用虚函数
void callVirtualFunction() {
BaseClass::virtualFunction();
}
};
// 函数模板
template <typename T>
void callVirtualFunc(T& obj) {
obj.virtualFunction();
}
不同继承方式下确保虚函数正确调用及运行时多态实现
- 公有继承:
- 在公有继承下,
TemplateClassPublic
从 BaseClass
公有继承,BaseClass
的公有和保护成员在 TemplateClassPublic
中保持原有访问权限。
- 函数模板
callVirtualFunc
可以直接调用 obj.virtualFunction()
,因为 virtualFunction
在 TemplateClassPublic
中是公有的,对象可以直接访问。
- 运行时多态通过虚函数表(vtable)实现。当创建
TemplateClassPublic
对象时,会为其分配一个虚函数表指针(vptr),指向 TemplateClassPublic
对应的虚函数表。在运行时,根据对象实际类型(即 TemplateClassPublic
)的虚函数表来调用正确的虚函数。
- 私有继承:
- 私有继承下,
BaseClass
的所有成员在 TemplateClassPrivate
中变为私有成员。
- 直接在
callVirtualFunc
中调用 obj.virtualFunction()
会导致编译错误,因为 virtualFunction
在 TemplateClassPrivate
中是私有的,外部无法访问。
- 为了确保虚函数的正确调用,可以在
TemplateClassPrivate
中提供一个公有接口函数(如 callVirtualFunction
),在该函数内部调用 BaseClass::virtualFunction()
。然后在 callVirtualFunc
函数模板中调用 obj.callVirtualFunction()
。
- 运行时多态同样通过虚函数表实现,尽管访问权限改变,但虚函数机制依然生效。
- 保护继承:
- 保护继承时,
BaseClass
的公有和保护成员在 TemplateClassProtected
中变为保护成员。
- 直接在
callVirtualFunc
中调用 obj.virtualFunction()
会失败,因为 virtualFunction
是保护的,外部无法访问。
- 类似私有继承,需要在
TemplateClassProtected
中提供一个公有接口函数(如 callVirtualFunction
)来调用 BaseClass::virtualFunction()
,然后在 callVirtualFunc
函数模板中调用 obj.callVirtualFunction()
。
- 运行时多态基于虚函数表机制正常工作。
编译器工作原理和潜在优化策略
- 编译器工作原理:
- 编译器在编译时,会为包含虚函数的类(如
BaseClass
)生成虚函数表。虚函数表是一个数组,存储类中虚函数的地址。
- 当类继承自含有虚函数的基类时,编译器会根据继承方式调整虚函数表。例如,在公有继承中,子类的虚函数表会继承基类虚函数表,并根据需要重写虚函数地址。
- 在对象创建时,编译器会为对象分配一个虚函数表指针(vptr),指向对应的虚函数表。
- 当调用虚函数时,编译器生成的代码通过对象的 vptr 找到虚函数表,再根据虚函数表中的地址调用实际的虚函数。
- 潜在优化策略:
- 内联虚函数:如果虚函数的实现比较简单,编译器可能会对其进行内联优化,直接将函数代码嵌入调用处,避免虚函数表查找的开销。
- 虚函数表指针优化:对于一些简单的继承结构,编译器可以在编译时确定虚函数的地址,从而避免运行时通过虚函数表查找,实现静态绑定优化。
- Devirtualization:在某些情况下,编译器通过分析代码,发现对象的实际类型在编译时可知,就可以将虚函数调用优化为普通函数调用,消除虚函数表查找的开销。