c++ - 通过 NULL 指针访问类成员

标签 c++ null virtual null-pointer

我正在尝试 C++,发现下面的代码非常奇怪。

class Foo{
public:
    virtual void say_virtual_hi(){
        std::cout << "Virtual Hi";
    }

    void say_hi()
    {
        std::cout << "Hi";
    }
};

int main(int argc, char** argv)
{
    Foo* foo = 0;
    foo->say_hi(); // works well
    foo->say_virtual_hi(); // will crash the app
    return 0;
}

我知道虚拟方法调用会崩溃,因为它需要vtable查找并且只能使用有效的对象。

我有以下问题

  1. 非虚方法 say_hi 如何作用于 NULL 指针?
  2. 对象 foo 在哪里分配?

有什么想法吗?

最佳答案

对象foo是一个类型为Foo*的局部变量。该变量可能会在 main 函数的堆栈上分配,就像任何其他局部变量一样。但存储在 foo 中的是一个空指针。它没有指向任何地方。任何地方都没有表示 Foo 类型的实例。

要调用虚函数,调用者需要知道该函数正在哪个对象上调用。这是因为对象本身决定了应该真正调用哪个函数。 (这通常是通过给对象一个指向 vtable 的指针、一个函数指针列表来实现的,调用者只知道它应该调用列表中的第一个函数,而无需事先知道该指针指向哪里。)

但是要调用非虚函数,调用者不需要知道所有这些。编译器确切地知道将调用哪个函数,因此它可以生成 CALL 机器代码指令以直接转到所需的函数。它只是将一个指向调用函数的对象的指针作为函数的隐藏参数传递。换句话说,编译器将您的函数调用转换为:

void Foo_say_hi(Foo* this);

Foo_say_hi(foo);

现在,由于该函数的实现永远不会引用其 this 参数所指向的对象的任何成员,因此您可以有效地避免取消引用空指针,因为您永远不会取消引用空指针。

正式而言,在空指针上调用任何函数(即使是非虚拟函数)都是未定义的行为。未定义行为允许的结果之一是您的代码似乎完全按照您的预期运行。 不应该依赖它,尽管您有时会发现编译器供应商提供的库确实依赖它。但编译器供应商的优势在于能够为未定义的行为添加进一步的定义。不要自己做。

关于c++ - 通过 NULL 指针访问类成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34048979/

相关文章:

c++ - 链接期间出现 CUDA 多重定义错误

SQL UPDATE,但仅当旧值为空时

swift - 在 swift 中执行 while == nil 最惯用的方法是什么?

C++虚函数执行效率

c++ - 是否有可能(如何)在 c/c++ 代码中给出编译器指令

c++ - 在内存充足但碎片化的环境下,new/malloc 是否会造成内存洗牌?

c++ - 标准宏命名约定

powershell - 如何在powershell中忽略foreach中的空值?

.net - 在 .NET 中实现虚拟文件系统

c++ - C++ 中的多重虚拟继承提示特定函数调用