检查ptr是不是属于虚拟类?

Posted

技术标签:

【中文标题】检查ptr是不是属于虚拟类?【英文标题】:Check if a ptr belongs to a virtual class?检查ptr是否属于虚拟类? 【发布时间】:2011-03-31 17:18:55 【问题描述】:

我的代码运行不正常,我可以用下面的代码迷你重现它。 (codepad link)

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

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

据我了解,this_test 应该为空。它不是。如何检查该虚拟 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

【问题讨论】:

Dummy* 已经是 Dummy*,因此可以优化演员阵容。 dynamic_cast 假设您没有在输入类型上撒谎(这就是 C-Cast。对编译器撒谎(它是一个虚拟指针;只有你必须相信我))。 @Martin:你知道强制检查的方法吗? void* 不起作用 为什么一定要查!你已经知道 0x123 不是指针。 而不是告诉我们你在做什么。试着告诉我们你想要达到的目标。 【参考方案1】:

一厢情愿... :)

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

【讨论】:

据我记忆,它使用虚拟表来推断类型。还启用了 rtti @acidzombie24:RTTI 与这里发生的事情有点不同,不,你有一个虚拟方法这一事实并不意味着什么,因为在地址 0x123 处没有虚拟表用于首先要查看的编译器,它肯定不在同一个层次结构中。对编译器撒谎是~危险的~。 @Mehrdad:这就是我需要这个的原因。我只有 void* 和数百个。它使用 c 弱类型。我需要一种方法来检查类型是否符合我的预期,这样它就不会在我身上爆炸。 -edit- 这个项目中可能有数千个 void *。 @acidzombie24:它只使用 C 弱类型? C有虚函数和RTTI? @Mehrdad:某些接口以void* 的形式表示,当您将它们与 C++ 代码一起使用时,您实际上可以将指针传递给 C++ 对象,只要它们之间没有被 C 操作,但只是按原样通过。【参考方案2】:

问题是dynamic_cast 期望:

一个空指针 一个有效的指针

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

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

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

注意:这里没有注意到虚方法的存在与否


EDIT:如果您可以修改正在传递的对象...

然后尝试使用通用的基类:

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);

注意:两种解决方案都有相同的缺点,它们只有在您精确 exact 类型时才有效(好吧,只要两个类型共享同一个虚拟表,如果派生类没有覆盖任何虚拟方法,包括析构函数,就会发生这种情况。

这远非真正的dynamic_cast 的力量。

【讨论】:

我喜欢这个答案。如果我遇到某种问题,使所有东西都使用相同的基类(可能与钻石问题有关),我会这样做 @acidzombie24: 如果你可以让所有东西都使用相同的基类并且不要忘记在转换为void*之前转换为该类(因为指针算术),然后不要使用这个 ;) 事实上,我会编辑我的答案以包含这个替代方案,我只是想(为什么?)你不能改变传入的对象...... 酷。等等……我要记得在变虚之前施法!!!这是很多工作的方式。实际上,这没有任何意义。如果所有类都使用该类作为基类并且没有菱形问题,那么基类应该是最顶层/第一个。那么如果没有,这不应该正常工作吗?我可以为每个类生成一个测试,检查转换为 base 之前和之后的 ptr 是否相同,但我不认为这会是一个问题? 好吧,所以我在您的代码之前查看了您的评论,您似乎同意这只是多重继承的问题(我实际上是这样做的,但 B 始终是第一位的)。 IIRC 我不能在任何我可以使用 dynamic_cast 的地方使用 static_cast。也许我记错了。对于您的主要更改,您使用 boost::noncopyable 和 to_void 函数。为什么不只使用operator void*() 而不是使用不可复制。无需更改代码,感觉很自然。 (除非你能找到问题。) .. 因为它会隐式转换为非空 void*,即使语义肯定不是您想要的。最后,关于static_castdynamic_cast:动态转换允许您动态检查它是否是某种类型,而静态转换假设您知道要转换的内容。它们当然不能互换,我只是觉得在你的情况下你完全知道你想输入什么,如果不是这样,那么使用dynamic_cast【参考方案3】:

你从引用中跳过了一句话:

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

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

【讨论】:

你是说我应该用 int ptr 测试吗?因为那不起作用codepad.org/xAoLlR4y(我很想-1) @acidzombie24 - 你可以,编译器会变成总是返回一个空指针,因为 int* 也不能合法地指向一个 Dummy。为了有用,指针应该是指向 Dummy 基类的指针(如果有的话)。 @acidzombie24 - 检查了你的链接。问题是你的 C 风格演员。使用(Dummy *),您告诉编译器您肯定知道该指针属于该类型。它必须相信你! 这就是我的问题。 c代码(lib,不是我的)传递了很多指针,很容易把它们弄混,或者除了一些错误。我的函数认为它是一个 Dummy,但它可能是别的东西 这是个大问题。如果类型信息丢失,则它丢失。对不起!【参考方案4】:

实际上dynamic_cast 只适用于多态类型(通常这意味着它们必须有一个 vtable)。由于您使用 C-cast 向编译器断言类型为 Dummy*,因此它相信您。由于您随后在随机内存位置执行身份 dynamic_cast,因此它不能/无法进行类型检查。

但说真的,99% 的时间都不会尝试测试某事物是否属于特定类型。设计你的类,让基类定义一个接口,子类实现它,允许在没有大量转换的情况下使用(这是一种设计味道)。

【讨论】:

问题是我使用的 C 代码会丢失所有类型,而我得到的只是整数和 void*。所以我需要确保我所有的函数和参数都是我期望的类型。生成了大部分代码,但现在我遇到了一个错误,结果发现一个参数不是我期望的类型。但我也没有得到一个空 ptr,所以我的断言没有被触发。因此它更难追踪(幸运的 vc 在所有正确的地方爆炸)【参考方案5】:

dynamic_cast 在您将其用于向上转换或身份转换(转换为相同类型)时,不会执行任何运行时检查。对于此类转换,dynamic_cast 的行为与普通的static_cast 完全相同。没什么区别。

您链接的页面没有提到这一点,即甚至不是dynamic_cast 的远程完整规范,这使得它非常无用。

C++ 没有提供方法来确定给定指针是否实际上是指向给定类型的有效指针。所以,你在那里不走运。实现你自己的检查方法。

【讨论】:

以上是关于检查ptr是不是属于虚拟类?的主要内容,如果未能解决你的问题,请参考以下文章

如何在wordpress中检查帖子是不是属于分类类别的子类别

Java:检查异常与未检查异常

当内核使用过度使用内存时,是不是需要在分配内存后检查 NULL

检查列表的所有元素是不是属于同一类型

检查指针是不是为空,然后在同一个 if 语句中取消引用它是不是安全?

检查文件是不是属于某种类型