从基类指针调用空虚函数失败

时间:2016-03-09 19:48:25

标签: c++ inheritance virtual

在编写我的第一个软件时,我在使用虚函数,继承和指针时遇到了“奇怪”的行为。我所拥有的是模板类State,它在公共接口中具有以下功能

void State::saveState() {};

当应用程序首次启动时,为了设置默认状态,它在管理器函数中调用setState()函数,大致如下所示

case(some enum):
{
    mActiveState->saveState();
    delete mActiveState; 
    mActiveState = new SomeStateClassDerivedFromState();
    mActiveState->setup(some arguments);
    break;
}

现在对于奇怪的事情 - 此代码的前两行应该导致运行时错误或类似的东西。如果此函数用于更改状态,那么它应该没问题。但它也用于为State::mActiveState分配一些对象/内存,此时为nullptr。但由于某些原因它没有崩溃,程序按预期执行。但是,我想在其中一个继承自State的类中覆盖saveState()函数,所以我在函数中添加了一个虚拟标记(正文保持为空),并为saveState()写了一个定义。派生类。突然,这两行在启动时开始崩溃应用程序(正如预期的那样)。从saveState()删除虚拟代码会使应用再次运行。

为什么没有调用成员函数并删除nullptr导致崩溃,直到saveState()成员函数被声明为虚拟(空体,而不是纯虚拟)?

此代码重新创建问题

class State
{
public:
void saveState() {};
}

class DState : public State
{
saveState() {};
}

int main()
{
State* state = nullptr;
state->saveState();
delete state;
state = new DState();
}

这很有用。我将虚拟标记添加到State类中的saveState()函数的那一刻,它在启动时崩溃了。我的问题,为什么?

2 个答案:

答案 0 :(得分:3)

class State
{
public:
    void saveState() {};
}
//...
    State* state = nullptr;
    state->saveState();
//...

因为saveState在这里是非虚拟的,所以你只是调用一个静静地将this指针作为第一个参数的函数。

该函数什么都不做,所以什么都没发生,实际上编译器甚至可以完全忽略函数和调用它。如果你的函数试图引用一个成员变量,它就会崩溃。您基本上调用了未定义的行为并且很幸运 - 主要是因为您在函数中没有做任何事情。

当你使虚函数成功时,程序必须取消引用该对象以找到它的 vtable (虚函数表),这意味着它取消引用nullptr,这就是为什么你崩溃了。

你还问为什么删除一个空指针不会崩溃:那是因为,与free不同,删除空指针是合法的 - 基本上删除会对你进行检查。

答案 1 :(得分:1)

通过nullptr调用空方法具有未定义的行为,但不必使程序崩溃,因为没有数据被访问。调用虚函数虽然需要查找vtable,这会带来你看到的行为。但即使你的程序没有崩溃,它也不会使它有效。

另一方面,删除nullptr有效并导致无操作。