C++ 虚拟函数表何时设置为0

C++ 虚拟函数表何时设置为0,c++,visual-c++,C++,Visual C++,我正在分析应用程序中发生的访问冲突,因为它试图调用虚拟函数表指向0的对象上的虚拟函数。所以我想知道在对象生命周期中的什么时候,虚函数表指针没有设置或显式设置为零 (使用Visual C++ 10作为编译器)< P>当对象处于有效状态时,VTABLE指针将永远不会是0。在构造和销毁过程中,虽然对象具有抽象基类的动态类型,但vtable将指向抽象基类vtable,其中将包含纯虚拟函数,但vtable指针本身仍然是非零的 vtable指针只能在构建之前或销毁之后为零。当对象处于有效状态时,vtable

我正在分析应用程序中发生的访问冲突,因为它试图调用虚拟函数表指向0的对象上的虚拟函数。所以我想知道在对象生命周期中的什么时候,虚函数表指针没有设置或显式设置为零


(使用Visual C++ 10作为编译器)

< P>当对象处于有效状态时,VTABLE指针将永远不会是0。在构造和销毁过程中,虽然对象具有抽象基类的动态类型,但vtable将指向抽象基类vtable,其中将包含纯虚拟函数,但vtable指针本身仍然是非零的


vtable指针只能在构建之前或销毁之后为零。

当对象处于有效状态时,vtable指针永远不会为0。在构造和销毁过程中,虽然对象具有抽象基类的动态类型,但vtable将指向抽象基类vtable,其中将包含纯虚拟函数,但vtable指针本身仍然是非零的


vtable指针只能在构建之前或销毁之后为零。

如果vtable指针确实为零,则可能使用了VC++扩展,例如:

class __declspec(novtable) Base {
public:
    Base();
    virtual void proc();
};
这里
\uu declspec(novtable)
告诉编译器该类不需要vtable,因为在派生类安装了自己的vtable之前,不会对其调用任何虚拟函数。如果在这之前调用Base::proc(),则可能会出现错误

更可能的情况是vtable本身不是零,但函数的插槽是零,因为函数是纯虚拟的:

class Base {
public:
    virtual void proc() = 0;
};
然后有人用如下代码调用它:

void Derived::proc() {
    Base::proc();
    // Derived-specific stuff here.
};
这可能是因为派生版本的作者假定其重写需要调用基础版本,即使基础版本不存在

不管是哪种方式,找到这一点的一种方法是停止所有的基类诡计,正常地定义函数,并查看调用它的内容。例如:

class Base {
public:
    virtual void proc() {
        assert( typeof(*this) != typeof(Base) ); // Break-point here.
    }
};

如果vtable指针确实为零,则可能使用了VC++扩展,例如:

class __declspec(novtable) Base {
public:
    Base();
    virtual void proc();
};
这里
\uu declspec(novtable)
告诉编译器该类不需要vtable,因为在派生类安装了自己的vtable之前,不会对其调用任何虚拟函数。如果在这之前调用Base::proc(),则可能会出现错误

更可能的情况是vtable本身不是零,但函数的插槽是零,因为函数是纯虚拟的:

class Base {
public:
    virtual void proc() = 0;
};
然后有人用如下代码调用它:

void Derived::proc() {
    Base::proc();
    // Derived-specific stuff here.
};
这可能是因为派生版本的作者假定其重写需要调用基础版本,即使基础版本不存在

不管是哪种方式,找到这一点的一种方法是停止所有的基类诡计,正常地定义函数,并查看调用它的内容。例如:

class Base {
public:
    virtual void proc() {
        assert( typeof(*this) != typeof(Base) ); // Break-point here.
    }
};

如果您能够在代码中复制问题,那么解决问题可能会更容易。也许你有一个UB的实例,是因为误用了一个正在构建的对象。这感觉像是错误的方法。首先通过内存调试器运行您的程序,并确定您是否在重复删除某些内容,或是类似的内容。如果可以,我会这样做,但不幸的是,问题没有出现在我们的开发系统上,因此我只需要分析转储文件。这是堆损坏的典型结果。v-table指针非常容易受到攻击,因为它是对象中的第一个字段,所以一个小的缓冲区覆盖足以打击它。内存损坏当然也在我的可能原因列表中,但在我看来,它不像是典型的内存损坏,所以我想研究其他可能性,如果你能在代码中复制你的问题,解决你的问题可能会更容易。也许你有一个UB的实例,是因为误用了一个正在构建的对象。这感觉像是错误的方法。首先通过内存调试器运行您的程序,并确定您是否在重复删除某些内容,或是类似的内容。如果可以,我会这样做,但不幸的是,问题没有出现在我们的开发系统上,因此我只需要分析转储文件。这是堆损坏的典型结果。v-table指针非常容易受到攻击,因为它是对象中的第一个字段,所以一个小的缓冲区覆盖足以打击它。内存损坏当然也在我的可能原因列表中,但在我看来,它不像是典型的内存损坏,所以我想研究其他可能性,我的类的最后一个析构函数会显式地将vtable指针设置为零吗?然后我会假设我正在处理一个应该被删除的对象,但在它被内存覆盖之前仍然或多或少保持不变?它不需要这样做(一旦对象被完全破坏,对其状态没有要求),但这样做可以帮助捕获错误。另一种方法是内存损坏。就是这样:对象已被销毁,但来自另一个线程,因此访问冲突受到竞速条件的影响。那么,我类的最后一个析构函数是否会显式将vtable指针设置为零?然后我会假设我正在处理一个应该被删除的对象,但在它被内存覆盖之前仍然或多或少保持不变?它不需要这样做(一旦对象被完全破坏,对其状态没有要求),但这样做可以帮助捕获错误。另一种选择是内存损坏。就是这样:对象已被销毁,但来自另一个线程,因此访问冲突受到竞速条件的约束。这两者都不是。调试器显式地将vtable指针显示为零,因此甚至无法显示单个条目。这两种情况都不存在。调试器显式地将vtable指针显示为零,因此甚至无法显示单个条目。