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

int main()
{
    A *a = NULL;
    a->mprint();
    return 0;
}

我的输出为“TESTING NULL POINTER”。谁能解释为什么这个程序正在打印输出而不是崩溃。我在 Dev C++ 和 aCC 编译器上检查它都给出了相同的结果。

4

6 回答 6

26

您没有使用任何成员变量A- 该函数完全独立于A实例,因此生成的代码恰好不包含取消引用 0 的任何内容。这仍然是未定义的行为- 它可能恰好适用于某些编译器。未定义的行为意味着“任何事情都可能发生”——包括程序恰好按照程序员的预期工作。

例如,如果您使用make mprintvirtual,您可能会遇到崩溃——或者如果编译器认为它并不真正需要vtable,您可能无法获得崩溃。

如果将成员变量添加到 A 并打印它,您将遇到崩溃。

于 2011-03-25T10:51:38.893 回答
7

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

但是,它起作用的原因是非虚成员函数通常被实现为将“this”指针作为隐式第一个参数的常规函数​​。因此,如果您在空指针上调用成员函数,只要您不使用 this 指针,您的程序就不会崩溃。当然,你不能依赖这个;有效的 C++ 编译器可能会导致崩溃。

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

于 2011-03-25T10:55:13.177 回答
5

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

在这种情况下,很可能是因为它像这样重写了您的函数

void mprint(A* this);

你的电话是这样的

mprint(0);

所以它只是把它当作一个普通函数来调用,并将空指针作为参数传递,然后你永远不会以任何方式实际使用它。这就解释了为什么它不会崩溃,但编译器几乎可以自由地做任何事情

于 2011-03-25T10:52:37.530 回答
2

简单回答:因为mprint()没有使用类的任何成员变量

详细解答:当一个类的方法被调用时,类实例被传递给被调用函数(通常作为第一个参数,但是在一些调用约定如__thiscall 中,this 是在寄存器中传递的)。此类实例用于访问被调用方方法中使用的所有成员变量。

在这种情况下,此实例为 NULL 但这没有任何区别,因为在被调用方方法中没有使用任何成员变量。尝试更改代码,以便在mprint()方法中打印成员变量的值,您将遇到崩溃。

于 2011-03-25T10:50:40.380 回答
1

能够在无效指针上调用非虚拟成员函数甚至可以在指针本身中编码与对象关联的信息。例如:

#include <iostream>

class MagicInteger {

  public:

   static MagicInteger* fromInt (int x) {
     return reinterpret_cast<MagicInteger*>(x);
   }

   int getValue() {
     return static_cast<int>(reinterpret_cast<intptr_t>(this));
   }       

  private:
   // forbid messing around
   MagicInteger ();  
   MagicInteger (MagicInteger&);
   MagicInteger& operator=(const MagicInteger&);
};

int main (void) {
  MagicInteger* i = MagicInteger::fromInt(6);
  std::cout << "Value is " << i->getValue() << std::endl;
  return 0;
}

这也可以用于实现标记指针,即包含有关被指点的元信息的指针。

这两个成语在 Google Chrome 的 javascript VM V8 中用于表示 31 位整数

于 2011-03-26T20:19:20.637 回答
0

这是完全合法的电话。

让我们了解它是如何工作的

当一个新对象被创建时,它的成员变量被创建。

成员函数呢?成员函数没有分配消息,所有成员函数总是有一份副本。默认情况下,每个成员函数都会添加一个成员变量,即指向对象本身的 this 指针。
当不存在对象时,对象指针为空值。这并不重要,因为您没有以任何方式访问它。如果在方法中使用任何成员变量的指针,就会遇到问题。这是因为成员变量在空指针的情况下无效。

在 MFC 中,我们有用于 CWnd 的 GetSafeHwnd() 方法。这工作原理相同。

于 2011-03-25T12:47:11.363 回答