有什么理由喜欢static_cast而不是一系列隐式转换?

时间:2011-02-10 08:31:48

标签: c++ casting

假设我有一个实现多个接口的类

class CMyClass : public IInterface1, public IInterface2 { }; 

并且在该类的成员函数中,我需要获得指向其中一个接口的void*指针(IUnknown::QueryInterface()中的典型情况。

典型的解决方案是使用static_cast来实现指针调整:

void* pointer = static_cast<IInterface2*>( this );

如果没有从CMyClass继承的已知类,则在这种情况下是安全的。但如果存在这样的类:

class CDerivedClass : public CUnrelatedClass, public CMyClass {};

我意外地做了

void* pointer = static_cast<CDerivedClass*>( this );

this实际上是一个指向编译器不会抓住我的CMyClass实例的指针,程序可能会在以后遇到未定义的行为 - static_cast变得不安全。

建议的解决方案是使用隐式转换:

IInterface2* interfacePointer = this;
void* pointer = interfacePointer;

看起来这样可以解决这两个问题 - 指针调整和无效向下转发的风险。

第二种方案有问题吗?可能有什么理由更喜欢第一个?

5 个答案:

答案 0 :(得分:3)

您可以使用此模板:

template<class T, class U> T* up_cast(U* p) { return p; }

用法:

struct B {};
struct C : B {};

int main()
{
  C c;

  void* b = up_cast<B>(&c);
}

请注意,'*'是隐含的。如果您更喜欢up_cast<B*>,请相应调整模板。

答案 1 :(得分:2)

分配给void *总是不安全的。无论你怎么写它都会搞砸 - 假设用户试图为Interface1做QI,那么下面的任何一个都不会是警告或错误:

Interface2* interfacePointer = this;
void* pointer = interfacePointer;

void* pointer = static_cast<Interface2*>( this );

鉴于意外使用static_cast进行强制转换的风险很小,在一个很可能甚至无法访问派生类定义的文件中,我看到了很多额外的努力,实际安全性很小。

答案 2 :(得分:2)

除了以下事实之外我没有任何理由不使用后一种解决方案,如果其他人正在阅读您的代码,它将立即与您沟通,因为您使用了这样一个错综复杂的陈述(“为什么不是他只是使用static_cast?!?“),所以最好对它进行评论或使意图非常清晰。

答案 3 :(得分:1)

您的分析对我来说听起来很合理。不使用隐式方法的原因并不引人注目:

  • 稍微详细一点
  • 留下变量
  • 的static_cast&LT;&GT;可以说是更常见的,因此更有可能对其他开发人员显而易见,搜索等等。
  • 在很多情况下,即使派生类的声明也不会出现在基类函数的定义之前,因此不存在此类错误的可能性

答案 4 :(得分:1)

如果您害怕偶然使用static_cast做某事,那么我建议您将转换/界面指针获取到一些模板函数中,例如:像这样:

template <typename Interface, typename SourceClass>
void set_if_pointer (void * & p, SourceClass * c)
{
  Interface * ifptr = c;
  p = ifptr;
}

或者,使用dynamic_cast并检查NULL指针值。

template <typename Interface, typename SourceClass>
void set_if_pointer (void * & p, SourceClass * c)
{
  p = dynamic_cast<Interface *>(c);
}