虚函数表
让我们先了解一下虚函数表的背景知识以及它们的工作原理 (source):
[20.3] 虚拟和非虚拟有什么区别
成员函数被调用了吗?
非虚拟成员函数是静态解析的。那就是
成员函数是基于静态选择的(在编译时)
指向对象的指针(或引用)的类型。
相比之下,虚成员函数是动态解析的(在
运行)。也就是说,成员函数是动态选择的(在
运行时)基于对象的类型,而不是对象的类型
指向该对象的指针/引用。这称为“动态绑定”。
大多数编译器使用以下技术的一些变体:如果
对象有一个或多个虚函数,编译器把一个隐藏的
对象中的指针称为“virtual-pointer”或“v-pointer”。这
v-pointer 指向一个名为“virtual-table”的全局表或
“v-table”。
编译器为每个至少有一个的类创建一个 v-table
虚函数。例如,如果类 Circle 具有虚函数
对于 draw() 和 move() 和 resize(),只有一个 v-table
与类 Circle 相关联,即使有一个 gazillion Circle
对象,并且每个 Circle 对象的 v 指针将指向
到 Circle v 表。 v-table 本身有指向每个
类中的虚函数。例如,Circle v-table 将
有三个指针:一个指向 Circle::draw() 的指针,一个指向
Circle::move(),以及指向 Circle::resize() 的指针。
在一个虚函数的调度过程中,运行时系统遵循
对象的 v 指针指向类的 v 表,然后跟随
v-table 中的适当槽位到方法代码。
上述技术的空间成本开销是名义上的:额外的
每个对象的指针(但仅适用于需要动态处理的对象
绑定),每个方法加上一个额外的指针(但仅适用于虚拟
方法)。时间成本开销也相当小:与
普通函数调用,一个虚函数调用需要两个额外的
fetches(一个获取 v-pointer 的值,第二个获取
方法的地址)。此运行时活动均未发生
非虚函数,因为编译器解析非虚函数
仅在编译时基于类型的函数
指针。
我的问题,或者我是怎么来到这里的
我现在正尝试将类似的东西用于具有模板化优化加载函数的立方体文件基类,这些函数将针对不同类型的立方体(一些按像素存储,一些按图像存储等)以不同方式实现。
一些代码:
virtual void LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
我希望它是什么,但由于虚拟模板组合而无法编译:
template<class T>
virtual void LoadCube(UtpBipCube<T> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
我最终将模板声明移至类级别。这种解决方案会强制程序在读取数据之前了解它们将读取的特定类型的数据,这是不可接受的。
解决方案
警告,这不是很漂亮,但它允许我删除重复的执行代码
1) 在基类中
virtual void LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
2) 在子类中
void LoadCube(UtpBipCube<float> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }
void LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }
void LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }
template<class T>
void LoadAnyCube(UtpBipCube<T> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1);
请注意,LoadAnyCube 未在基类中声明。
这是另一个解决方法的堆栈溢出答案:
need a virtual template member workaround。