什么情况下vtable指针可以为null(或0x1)?
我目前正在调试崩溃日志。发生崩溃是因为(c++-)对象的 vtable 指针是 0x1,而据我从崩溃日志来看,对象的其余部分似乎没问题。
当程序尝试调用虚拟方法时会崩溃。
我的问题:什么情况下vtable指针会变成null?删除操作符是否将 vtable 指针设置为空?
在使用 gcc 4.0.1(Apple Inc. build 5493)的 OS X 上会发生这种情况。
I am currently debugging a crashlog. The crash occurs because the vtable pointer of a (c++-) object is 0x1, while the rest of the object seems to be ok as far as I can tell from the crashlog.
The program crashes when it tries to call a virtual method.
My question: Under what circumstances can a vtable pointer become null? Does operator delete set the vtable pointer to null?
This occurs on OS X using gcc 4.0.1 (Apple Inc. build 5493).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
可能是内存践踏 - 错误地写入了
vtable
。在 C++ 中,有几乎无数种方法可以“实现”这一目标。例如,缓冲区溢出。Could be a memory trample - something writing over that
vtable
by mistake. There is a nearly infinite amount of ways to "achieve" this in C++. A buffer overflow, for example.您的任何未定义行为都可能导致这种情况。例如:
另请参阅问题 实际可能的未定义行为的最糟糕示例是什么? 和 什么C++ 程序员应该了解所有常见的未定义行为吗?。
最好的选择是使用边界和内存检查器,作为繁重调试的辅助。
Any kind of undefined behaviour you have may lead to this situation. For example:
See also the questions What’s the worst example of undefined behaviour actually possible? and What are all the common undefined behaviour that a C++ programmer should know about?.
Your best bet is to use a bounds and memory checker, as an aid to heavy debugging.
一个非常常见的情况:尝试从构造函数调用纯虚方法...
构造函数
现在,假设以下
Interface()
实现很好:
然后一切都 在构造函数之后调用 pure 很痛苦,最好分解代码,对吗?
然后我们就可以一行完成!
为什么 ?
因为对象是自下而上构建的。我们来看一下。
构建
具体
类的说明(粗略)分配足够的内存(当然),也为_vtable分配足够的内存(每个虚拟函数1个函数指针,通常按照声明的顺序, )
调用
Concrete
构造函数(你看不到的代码)一个>调用
Interface
构造函数,该构造函数使用其指针初始化 _vtableb>调用
Interface
构造函数的主体(您编写的)c>覆盖这些方法的 _vtable 中的指针具体覆盖
d>调用
Concrete
构造函数的主体(你写的)那么问题是什么?好吧,看看
b>
和c>
顺序;)当您从构造函数中调用
virtual
方法时,它不会执行以下操作你所希望的。它确实会转到 _vtable 来查找指针,但 _vtable 尚未完全初始化。因此,重要的是,以下效果实际上是:
当从构造函数中调用虚拟方法时,您不需要正在构建的对象的完整动态类型,而是调用当前构造函数的静态类型。
在我的
Interface
/Concrete
示例中,它表示Interface
类型,并且该方法是纯虚拟的,因此 _vtable 不保存真正的指针(例如,0x0 或 0x01,如果您的编译器足够友好,可以设置调试值来帮助您)。析构函数
巧合的是,让我们检查一下析构函数的情况;)
那么析构时会发生什么?好吧,_vtable 工作得很好,并且调用了真正的运行时类型...然而,它在这里意味着未定义的行为,因为谁知道
m_data
在被删除之后和Interface< 之前发生了什么/code> 析构函数被调用?我不;)
结论
永远不要从构造函数或析构函数中调用虚拟方法。
如果不是这样,你就会遇到内存损坏,运气不好;)
A very common case: trying to call a pure virtual method from the constructor...
Constructors
Now, suppose the following implementation of
Interface()
Then everything is fine:
It's such a pain to call pure after the constructor, it would be better to factorize the code right ?
Then we can do it in one line!!
Why ?
Because the object is built bottom up. Let's look at it.
Instructions to build a
Concrete
class (roughly)Allocate enough memory (of course), and enough memory for the _vtable too (1 function pointer per virtual function, usually in the order they are declared, starting from the leftmost base)
Call
Concrete
constructor (the code you don't see)a> Call
Interface
constructor, which initialize the _vtable with its pointersb> Call
Interface
constructor's body (you wrote that)c> Override the pointers in the _vtable for those methods Concrete override
d> Call
Concrete
constructor's body (you wrote that)So what's the problem ? Well, look at
b>
andc>
order ;)When you call a
virtual
method from within a constructor, it doesn't do what you're hoping for. It does go to the _vtable to lookup the pointer, but the_vtable
is not fully initialized yet. So, for all that matters, the effect of:is in fact:
When calling a virtual method from within a Constructor, you don't the full dynamic type of the object being built, you have the static type of the current Constructor invoked.
In my
Interface
/Concrete
example, it meansInterface
type, and the method is virtual pure, so the _vtable does not hold a real pointer (0x0 or 0x01 for example, if your compiler is friendly enough to setup debug values to help you there).Destructors
Coincidently, let's examine the Destructor case ;)
So what happens at destruction ? Well the _vtable works nicely, and the real runtime type is invoked... what it means here however is undefined behavior, because who knows what happened to
m_data
after it's been deleted and beforeInterface
destructor was invoked ? I don't ;)Conclusion
Never ever call virtual methods from within constructors or destructors.
If it's not that, you're left with a memory corruption, tough luck ;)
我的第一个猜测是某些代码正在使用 memset() 来处理类对象。
My first guess would be that some code is
memset()
'ing a class object.这完全取决于实现。然而,可以非常安全地假设删除后某些其他操作可能会将内存空间设置为空。
其他可能性包括通过一些松散指针覆盖内存 - 实际上在我的情况下几乎总是这样......
也就是说,您永远不应该在删除后尝试使用对象。
This is totaly implementation dependant. However it would be quite safe to assume that after delete some other operation may set the memory space to null.
Other possibilities include overwrite of the memory by some loose pointer -- actually in my case it's almost always this...
That said, you should never try to use an object after delete.