“dynamic_cast”之后的空指针是否可以被解引用?

4
以下代码可以正确编译并获得神秘的输出:

特殊投资函数 00000000

(环境:C++ VS2010)
#include <iostream>
#include <vector>
using namespace std;

class Security {
public:
  virtual ~Security() {}
};

class Stock : public Security {};

class Investment : public Security {
public:
  void special() {
    cout << "special Investment function" << endl;
  }
};

int main() {
  Security* p = new Stock;
  dynamic_cast<Investment*>(p)->special();
  cout << dynamic_cast<Investment*>(p) << endl;
  return 0;
}

怎么可能呢?解引用一个空指针,却能得到“正确”的输出而不是崩溃?这是VS2010的特殊“特性”吗?

现在我明白了。我进行了一次测试,发现在“特殊”函数中解引用“this”会导致程序崩溃。

感谢您的帮助。

8个回答

7
对空指针进行解引用是未定义的行为 - 您可能会得到意想不到的结果。请参见这个非常相似的问题
在这种情况下,Investment::special() 以非虚拟方式调用,因此您可以将其视为编译器只创建了一个全局函数。
Investment_special_impl( Investment* this )

并将其称为传递一个空的this指针作为隐式参数。

您不应该依赖这个。


4

这是“未定义的行为”。将方法视为具有一个隐式参数,携带着“this”。在您的情况下,NULL被传递作为“this”的实际参数。由于您没有引用任何由“this”隐式或显式引用的对象数据,因此它没有崩溃。

如果该方法是虚拟的,它很可能会崩溃,因为虚拟调用通常通过与对象相关联的查找表进行分派(因此也涉及到“this”)。

由于编译器编写者可以自由地实现“this”和虚拟成员查找表,因此您不应该依赖于这种行为。它是未定义的。


是的,我在VS2010中进行了测试,如果函数是虚拟的,程序会崩溃。 - Richard

2
在大多数C++的实现中,非虚方法不需要一个有效的实例即可被调用(不检查this更快,因为标准并不要求)。如果您不访问实例字段,则可以将指针设置为NULL,方法仍然会成功。
虚方法需要有效的vtable,因此它们总是解引用对象,并在未初始化时导致错误。

2

这里没有对空指针进行解引用操作:你只是调用了非虚函数Investment::special(NULL),在其内部并没有对this进行解引用。虽然规范可能会说明这是未定义行为,但从编译器不在此处进行任何解引用以避免程序崩溃来看,这是完全合理的。


我认为这大约正确了50%。从技术上讲,Turner正在取消引用空指针,因为dynamic_cast是非法的,这将指针转换为null指针。无论虚拟与否,在null指针上进行此调用仍然是非法的。然而,恰好该成员函数根本不使用任何类数据,所以它偶然不会崩溃(可能编译器也优化了此调用)。 - Damon

1

对任何 NULL 指针进行解引用都是未定义行为。


1

你不应该取消引用 NULL 指针,因为这可能会导致未定义的行为。你的代码之所以能够工作,是因为在这个方法中:

void special() {
  cout << "special Investment function" << endl;
}

你真的没有引用this。为了演示这一点,只需在Investment类中声明一个变量,并尝试在special()内打印它。你会得到一个崩溃。


0
解除引用空指针会调用未定义的行为,无论您如何获取空指针,无论是作为dynamic_cast的结果还是其他原因。

0

我不确定关于解除引用NULL的问题,但是代码的行为我可以解释。

dynamic_cast< Investment* >(p)->special();

输出:"特殊投资功能"

cout << dynamic_cast < Investment* >(p) << endl;

输出:"0xABABABA" <- 实例p的内存地址

就像运行:

cout << p << endl;


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接