调用虚方法时崩溃

Crashing on calling virtual methods

本文关键字:崩溃 方法 调用      更新时间:2023-10-16

我在其他问题中读到过,但没有一个是类似的,有些是关于在构造函数中调用虚方法,有些是关于纯虚方法,但这里的问题是关于非纯的虚方法,而是关于不需要在所有派生类中实现的虚方法。如果实例化的类没有实现这个方法,如果我们调用,它会逻辑地从基类调用这个方法,有时它会崩溃。我在想,为什么?什么是VTABLE(它进入的地方)?解决这个问题的最好方法是什么?

这是一个简单的例子,(避免回答像纯虚拟)

#include <iostream>
class Foo
{
public:
    virtual std::string myString() {}
};
class Bar : public Foo
{
public:
};
int main(int argc, char ** argv)
{
    Foo * bar = new Foo;
    bar->myString();
    return 0;
}

最好的解决方案是什么?

  1. 抛出异常
  2. 使用断言(false)
  3. 返回默认值
  4. 避免实现body,它会导致错误编译时间
  5. 没有替代

最好的答案是解释为什么基于VTABLE会发生这种情况,当然,选择一个解决方案并解释原因。这个想法不是建立在意见的基础上。

基类确实实现了这个函数,它只是实现错了。它与变量或任何复杂的东西无关。首选解决方案4(因为它可以防止构建错误的程序),如果不可能/不希望按顺序1或2。

请注意,该错误与虚函数或一般的继承无关(您没有使用Bar,您注意到了吗?)它甚至与类无关,但也会发生在任何独立的函数中。考虑:

#include <iostream>
#include <string>
// Note: UB -- nothing returned
int getInt() {}
std::string getStr() {}
int main(int argc, char ** argv)
{
    // This probably works (read access from an arbitrary 
    // location on the stack which is in the prog's address space)
    std::cout << getInt() << std::endl;
    // This will crash. operator<< will try to access memory through
    // a pointer value which is some arbitrary byte pattern on the stack.
    // Probably not in the prog's address space.
    // Then the destructor for the temporary string will 
    // try to delete the same 
    // memory which will crash in any case, even if it happens to
    // point to valid memory (which, alas, was never allocated).
    std::cout << getStr();
    std::cout << "Still alive?n"; // never printed
    std::cout.flush();
    return 0;
}

为了防止在原始代码中发生错误,只需返回一个值。如果你实现这个函数并且不抛出或中止(这是三个备选方案),也就是说,如果你返回,你必须返回一个值:

#include <iostream>
class Foo
{
public:
    virtual std::string myString() { return "testn";}
};
class Bar : public Foo
{
public:
};
int main(int argc, char ** argv)
{
    Foo * bar = new Foo();
    std::cout << bar->myString();
    return 0;
}

VTABLE是一个指向虚方法的指针表。通常,指向VTABLE的指针对视图是隐藏的,但它通常作为类实例中的第一个元素实现。

当派生类没有父类实现为虚的成员函数时,将调用父类的方法。

你的mystring函数应该返回一个字符串