当我通过C++中的空指针调用成员函数时,为什么程序不会崩溃?

use*_*367 16 c++ null member-functions

#include "iostream"
using namespace std;
class A
{
public:
    void mprint()
    {
        cout<<"\n TESTING NULL POINTER";
    }
};

int main()
{
    A *a = NULL;
    a->mprint();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我输出为"TESTING NULL POINTER".任何人都可以解释为什么这个程序打印输出而不是崩溃.我在Dev C++和aCC编译器上检查了它们都给出了相同的结果.

Eri*_*rik 26

您没有使用任何成员变量A- 该函数完全独立于A实例,因此生成的代码恰好不包含任何解除引用0的内容.这仍然是未定义的行为 - 它可能恰好适用于某些编译器.未定义的行为意味着"任何事情都可能发生" - 包括程序恰好像程序员所期望的那样工作.

如果你做mprint虚拟你可能会崩溃 - 或者你可能没有得到一个如果编译器发现它并不真的需要一个vtable.

如果将成员变量添加到A并打印出来,则会发生崩溃.

  • @Martin:你知道它没有的任何编译器吗?通常,当人们说"几乎所有编译器"时,它意味着"我想不出编译器做其他事情的任何好理由,也没有看到编译器做其他事情,但标准不需要它" .如果规则存在例外情况,那么简单的情况可能无处不在,但随后某些编译器会在更复杂的情况下通过优化来引导您.例如,在`mprint`中放置`if(this == 0)`,看看你最喜欢的编译器是否仍然打印优化. (2认同)

tem*_*def 7

根据C++规范,此程序具有未定义的行为,因为您在空接收器上调用成员函数.

但是,这种方法的作用是,非虚拟成员函数通常实现为常规函数,将"this"指针作为隐式的第一个参数.因此,如果在空指针上调用成员函数,只要不使用this指针,程序就不会崩溃.当然,你不能依靠这个; 有效的C++编译器可能会导致崩溃.

但是,虚函数是一个不同的故事,因为实际调用的函数需要在运行时解析.这通常涉及对接收器的虚函数表进行内省.因此,如果您尝试在空指针上调用虚拟成员函数,即使te函数不访问它,它仍将导致崩溃.如果你好奇的话,试试吧!


jco*_*der 5

使用指向对象的空指针调用成员函数的结果在c ++中是未定义的行为,因此它可以执行任何操作.

在这种情况下,它可能是因为它重写了你的功能,因为它就像这样

void mprint(A* this);
Run Code Online (Sandbox Code Playgroud)

和你这样的电话

mprint(0);
Run Code Online (Sandbox Code Playgroud)

所以它只是把它称为普通函数,并将空指针作为参数传递给你,你从来没有以任何方式实际使用它.这就解释了为什么它不会崩溃,但编译器可以自由地做任何事情