在复杂的代码库中,我有一个非虚拟基类指针数组(该基类没有虚拟方法)
考虑以下代码:
#include <iostream>
using namespace std;
class TBase
{
public:
TBase(int i = 0) : m_iData(i) {}
~TBase(void) {}
void Print(void) {std::cout << "Data = " << m_iData << std::endl;}
protected:
int m_iData;
};
class TStaticDerived : public TBase
{
public:
TStaticDerived(void) : TBase(1) {}
~TStaticDerived(void) {}
};
class TVirtualDerived : public TBase
{
public:
TVirtualDerived(void) : TBase(2) {}
virtual ~TVirtualDerived(void) {} //will force the creation of a VTABLE
};
void PrintType(TBase *pBase)
{
pBase->Print();
}
void PrintType(void** pArray, size_t iSize)
{
for(size_t i = 0; i < iSize; i++)
{
TBase *pBase = (TBase*) pArray[i];
pBase->Print();
}
}
int main()
{
TBase b(0);
TStaticDerived sd;
TVirtualDerived vd;
PrintType(&b);
PrintType(&sd);
PrintType(&vd); //OK
void* vArray[3];
vArray[0] = &b;
vArray[1] = &sd;
vArray[2] = &vd; //VTABLE not taken into account -> pointer not OK
PrintType(vArray, 3);
return 0;
}
输出为(在Win64上与Mingw-w64 GCC 4.9.2编译):
Data = 0
Data = 1
Data = 2
Data = 0
Data = 1
Data = 4771632
失败的原因是,每个TVirtualDerived实例都有一个指向虚拟表的指针,而TBase没有。因此,没有先前的类型信息(从void *到TBase *)向上广播到TBase是不安全的。
问题是,我一开始就无法避免将其转换为void *。在基类上添加虚拟方法(例如析构函数)是可行的,但是会占用内存(我想避免)
语境:
我们正在非常受限的环境(内存受到严格限制)中实施信号/插槽系统。由于我们有数百万个可以发送或接收信号的对象,因此这种优化是有效的(当然,在可行时)
问题:
我怎么解决这个问题?到目前为止,我发现:
1-在TBase中添加虚拟方法。可行,但是并不能真正解决问题,可以避免。而且效率低下(内存过多)
2-强制转换为TBase *,而不是强制转换为数组中的void *,这是以失去通用性为代价的。(可能接下来我会尝试)
您看到其他解决方案了吗?
您必须考虑如何在内存中布置类。TBase
很简单,一个成员只有四个字节:
_ _ _ _
|_|_|_|_|
^
m_iData
TStaticDerived
是一样的 但是,TVirtualDerived
完全不同。现在它的对齐方式为8,必须先从一个vtable开始,其中包含析构函数的条目:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
^ ^
vtable m_iData
所以,当你投vd
来void*
,然后TBase*
,您可以有效地重新诠释了前四个字节的虚函数表(偏移地址进入~TVirtualDerived()
)作为m_iData
。解决方案是先对static_cast
to执行操作TBase*
,该操作将返回一个指针,以指向TBase
in的正确起点vd
,然后返回void*
:
vArray[2] = static_cast<TBase*>(&vd); // now, pointer is OK
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句