上述问题的发生,根本原因就是接口指针指向了错误的地方,而导致这种错误的原因就是使用了强制类型转换。C++允许类型转换并能正确处理的是具有继承关系的类型的对象的类型转换,这也是多态的基础。C++编译器能够根据头文件中类的声明在类型转换时自动调整对象指针的位置,从而能够正确的实现多态。
然而如果C++编译器不能根据类声明推算出类型转换时的指针调整方式时,如果使用了强制类型转换,那么编译器只是简单的默默无作为,也就是根本就不调整指针位置,也不会警告开发者。这就导致了问题的发生。
解决思路有三个:
(1)不使用强制类型转换,使用static_cast进行编译期类型转换,此时如果C++编译期不能推算出指针调整算法,就会报错,提醒开发者。
这种方法可以提示开发者出现错误,但不能解决问题。
(2)不使用强制类型转换,使用dynamic_cast进行运行期动态类型转换,这需要开启编译器的RTTI,如下所示。
int main(int argc, char** argv)
{
I1* pI1 = CreateC();
pI1->vf1();
I2* pI2 = dynamic_cast<I2*>(pI1);
pI2->vf2();
delete pI1;
return 0;
}
此时,编译和运行都如预期一样,完全正确。缺陷就是开启RTTI会影响程序性能,而且好像VC++6中无法正常工作。
(3)某种程序上学习COM,提供接口查询功能。
#include <iostream>
#include <hash_map>
using namespace std;
class I1
{
public:
virtual void vf1()
{
cout << "I'm I1:vf1()" << endl;
}
};
class I2
{
public:
virtual void vf2()
{
cout << "I'm I2:vf2()" << endl;
}
virtual void vf3()
{
cout << "I'm I2:vf3()" << endl;
}
};
class C : public I1, public I2
{
private:
hash_map<string, string> m_cache;
};
I1* CreateC()
{
return new C();
}
I2* QueryInterface(I1* obj)
{
C* pC = static_cast<C*>(obj);
return static_cast<I2*>(pC);
}
I1* QueryInterface(I2* obj)
{
C* pC = static_cast<C*>(obj);
return static_cast<I1*>(pC);
}
int main(int argc, char** argv)
{
I1* pI1 = CreateC();
pI1->vf1();
I2* pI2 = QueryInterface(pI1);
pI2->vf2();
delete pI1;
return 0;
}
这种方式,既可以得到正确的运行结果,也不需要用户调用dynamic_cast,所以效果最好。但实现和调用都较为麻烦,使得库的使用不方便。
5 一点感想
(1)C++到处充满细节,使得开发者必须考虑很多细节,而且编译器有时候对开发者隐藏了很多东西,有时候又做的不好,使得这个语言做开发不太顺手,也许这就是C#,Java盛行的原因,C#中完全不存在上面说的问题,因为C#一定是运行时类型识别的。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
I1 pI1 = new C();
pI1.vf1();
I2 pI2 = (I2)pI1;
pI2.vf2();
}
}
interface I1
{
void vf1();
}
interface I2
{
void vf2();
}
class C : I1, I2
{
public void vf1()
{
Console.WriteLine("I'm vf1()");
}
public void vf2()
{
Console.WriteLine("I'm vf2()");
}
}
}
(2)开发库的时候,对外接口以类的形式是否合适?是否还是以纯粹的C函数为接口更简洁?C++的前途....