一般来说犯错误的都是开发者.VS的错误还是蛮少的.额..除了预编译头有时候会错误要Clean一下.
今天确实遇到了一个bug.查了半天.又写了好些测试代码.才把错误模型整理出来.
发现是在存在虚函数表时对于父类模板类的偏移计算错误.这句话说的挺绕口.
据说也和VS对代码限制较少有关.在GCC下是编译不过去的.
模型挺简单
template< class T >
class TestTClass
{
public:
T i;
std::vector<T> m_vec;
T GetNumber()
{
return i;
}
int GetSize()
{
return m_vec.size();
}
};
class TestClass : public TestTClass<int>
{
public:
TestClass()
{
i = 1;
}
int GetNumber()
{
return TestTClass::GetNumber();
}
int GetSize()
{
return TestTClass::GetSize();
}
};
这段代码中TestClass从TestTClass派生.并重写了两个函数.没有虚函数.这时没问题.测试代码
TestClass kClass;
kClass.i = 3;
kClass.m_vec.push_back( 1 );
int iTest = kClass.GetNumber();
int iSize = kClass.GetSize();
但如果将GetSize改成虚函数.
class TestClass : public TestTClass<int>
{
public:
TestClass()
{
i = 1;
}
int GetNumber()
{
return TestTClass::GetNumber();
}
virtual int GetSize()
{
return TestTClass::GetSize();
}
};
问题就出来了.此时跟踪到TestTClass::GetNumber函数中发现this指针与父类的This指针已经不一样了.整个内存已经乱了.i和m_vec已经和父类不是同一个i和m_vec了.
TestClass kClass;
kClass.i = 3;
kClass.m_vec.push_back( 1 );
int iTest = kClass.GetNumber();
int iSize = kClass.GetSize();
此时的iTest和iSize大概都等于0;
如果是虚继承的话 like this:
class TestClass : virtual public TestTClass<int>
又没有问题.
根据现象分析.虚继承时父类包含有指向子类的指针.地址已经固定了.所以正确.由此推断可能是由于根据虚函数表计算子类地址时有错误.
但有一点必须指出.
return TestTClass::GetNumber();
这样的写法是不严谨的写法.由于TestClass是模板.模板是编译器生成代码的.按道理必须指定类型.如果没有类型.生成的子类又是指向哪里呢?
假如写法都写成
return TestTClass<int>::GetNumber();
这样就不会发生这样的问题.据说GCC像上边的写法是编译不过去的.