检查ptr是否属于虚拟类?

时间:2011-03-31 17:18:56

标签: c++ casting

我的代码表现得很糟糕,我可以使用下面的代码进行迷你重现。 (codepad link

来自http://www.cppreference.com/wiki/keywords/dynamic_cast

  

如果您尝试强制转换为指针   类型,那种类型不是实际的   参数对象的类型,然后是   演员的结果将为NULL。

根据我的理解,this_test应为null。不是。我如何检查虚拟ptr是否实际上是虚拟对象的ptr?

#include <ios>
struct Dummy{ virtual void dummyfn(){} };

int main(){
Dummy* this_test = dynamic_cast<Dummy*>((Dummy*)0x123);
//assert(this_test==0);
cout << std::hex << this_test<<endl;
return 0;
}

输出:

0x123

5 个答案:

答案 0 :(得分:2)

一厢情愿...... :)

我相信dynamic_cast仅适用于多态情况中的向下投射,而不适用于任何投射。它不像编译器存储每个变量的类型信息,所以它不能做你想的 - 我很确定它是未定义的行为。

答案 1 :(得分:1)

你从你的引语中跳过了一句话:

  

dynamic_cast关键字将object从一个指针或引用类型转换为另一个,执行运行时检查以确保转换的有效性。

这里的问题是0x123不是指向对象的指针,所以它不起作用。

答案 2 :(得分:1)

问题是dynamic_cast要求:

  • 空指针
  • 有效指针

在这里你只能提供垃圾,所以它没用,而不是你想要的演员。

如果你得到void*,那么你可以使用reinterpret_cast(比C-cast更好,因为更明显)将其转换为另一种类型:

void* p = 0x123;
Dummy* dummy = reinterpret_cast<Dummy*>(p);

注意:此处不会注意到虚拟方法的存在与否


编辑:如果您可以修改传递的对象......

然后尝试使用公共基类:

struct Base: private boost::noncopyable { virtual ~Base() = 0 }; Base::~Base() {}

并定义以下帮助程序:

template <typename T>
void* to_void(T* t) {
  Base* base = t;
  return reinterpret_cast<void*>(base);
}

template <typename T>
T* from_void(void* p) {
  Base* base = reinterpret_cast<Base*>(p);
  return dynamic_cast<T*>(base);
}

前者非常重要,因为可能的指针调整(这可能只发生在多重继承的情况下)。

注意:如果你不使用虚拟继承或其他RTTI东西,可以在这里使用fast_cast

template <typename T, typename U>
T* fast_cast(U* u) {
#ifdef NDEBUG
  return static_cast<T*>(u);
#else
  return dynamic_cast<T*>(u);
#endif
}

如果无法做到这一点,可以采用以下解决方案,但我担心它们会让人感觉不舒服。

由于dynamic_cast在这里不能正常工作,你必须实际提出自己的类型检查机制。

一种方法可以是使用“存储库”,在其中注册所获得的void*指针以及关联的type_info对象。

typedef std::map<void*, std::type_info const*> Repository;

template <typename Dest>
Dest* dynamic_check(void* p, Repository const& rep) {
  Repository::const_iterator it = rep.find(p);
  assert(it != rep.end() && "dynamic_check: no such entry");

  assert(typeid(Dest) == *(it->second) && "dynamic_check: wrong type");

  return reinterpret_cast<Dest*>(p);
}

如果无法做到这一点,那么你可以破解C ++对象模型。如果您知道该对象至少有一个虚方法,那么它必须在我知道的所有编译器上有一个虚拟指针(VS,gcc,clang),并且该指针是该对象的前4/8字节。

inline void* virtual_pointer(void* p) {
  assert(p != 0 && "virtual_pointer: null");
  return reinterpret_cast<void*>(*p);
}

template <typename T>
void* virtual_pointer(T const& t) {
  return virtual_pointer(reinterpret_cast<void*>(&t));
}

template <typename T>
void* virtual_pointer() {
  static void* pointer = virtual_pointer(T());
  return pointer;
}


template <typename Dest>
Dest* dynamic_check(void* p) {
  assert(virtual_pointer<Dest>() == virtual_pointer(p));
  return reinterpret_cast<Dest*>(p);
}

注意:两种解决方案都存在相同的缺点,只有在您精确确定完全类型的情况下它们才能正常工作(好吧,只要有两个,你就可以使用它类型共享相同的虚拟表,如果派生类不覆盖任何虚方法,包括析构函数,则会发生这种情况。

这远不是真正dynamic_cast的力量。

答案 3 :(得分:0)

实际上dynamic_cast仅适用于多态类型(通常这意味着它们必须具有vtable)。由于您使用C-cast向编译器声明类型为Dummy*,因此它相信您。因为您在随机内存位置上进行了身份dynamic_cast,所以它不能/不能进行类型检查。

但严重的是,99%的时间都没有尝试测试某种特定类型的东西。设计你的类,使基类定义一个接口,子类实现它,允许使用而不需要大量的转换(这是一种设计气味)。

答案 4 :(得分:0)

当您将其用于upcast或identity-casts(转换为相同类型)时,

dynamic_cast不会执行任何运行时检查。对于此类演员,dynamic_cast的行为与普通static_cast完全相同。没有任何区别。

您链接的页面没有提及,即使是dynamic_cast的远程完整规范,这也使它变得毫无用处。

C ++没有办法确定给定指针实际上是否是指向给定类型的有效指针。所以,你在那里运气不好。实施自己的检查方法。