“dynamic_cast”之后的 NULL 指针实际上可以被取消引用吗?

Posted

技术标签:

【中文标题】“dynamic_cast”之后的 NULL 指针实际上可以被取消引用吗?【英文标题】:Can NULL pointer after "dynamic_cast" actually be dereferenced? 【发布时间】:2011-06-06 06:48:09 【问题描述】:

以下代码正确编译并得到神秘输出:

特殊的投资功能 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;

怎么可能?取消引用 NULL 指针并获得“正确”输出而不是崩溃? 是不是VS2010的特殊“特性”?

现在我明白了。我做了一个测试,似乎在“特殊”函数中取消引用“this”会导致程序崩溃。

感谢您的帮助。

【问题讨论】:

【参考方案1】:

取消引用空指针是未定义的行为 - 您可能会得到意想不到的结果。见this very similar question。

在这种情况下Investment::special()是以非虚拟方式调用的,所以你可以认为编译器只是创建了一个全局函数

Investment_special_impl( Investment* this )

并调用它传递一个空的this指针作为隐式参数。

你不应该依赖这个。

【讨论】:

【参考方案2】:

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

如果方法是虚拟的,它很可能会崩溃,因为虚拟调用通常是通过与对象关联的查找表(因此是“this”)调度的。

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

【讨论】:

是的,我在VS2010中测试过,如果函数是虚拟的,程序就会崩溃。【参考方案3】:

在 C++ 的大多数实现中,非虚拟方法不需要调用有效的实例(不检查 this 会更快,因为标准不需要它)。

您可以将指针设置为 NULL,如果您不访问实例字段,方法仍然会成功。

虚拟方法需要有效的vtable,因此它们总是取消引用对象并在未初始化的情况下导致错误。

【讨论】:

【参考方案4】:

你没有在这里取消引用空指针:你只需调用函数 Investment::special(NULL),它是非虚拟的,并且在它的主体中不会取消引用 this。尽管规范可能告诉这是未定义的行为,但编译器完全理智地不要在此处放置任何取消引用,这样程序就不会崩溃。

【讨论】:

我认为这大约是正确的 50%。从技术上讲,Turner 正在解引用空指针,因为 dynamic_cast 是非法的,它将指针变为空指针。对空指针的 this 调用仍然是非法的,无论它是否是虚拟的。然而,碰巧成员函数根本不使用任何类数据,所以它不会偶然崩溃(而且,编译器也可能优化了 this-call out)。【参考方案5】:

取消引用 any NULL 指针是未定义的行为。

【讨论】:

【参考方案6】:

您不应该取消引用 NULL 指针,因为它可能导致未定义行为。为什么您的代码有效是因为在方法中:

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

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

【讨论】:

【参考方案7】:

取消引用空指针会调用未定义的行为,无论您如何获得空指针,无论是由于dynamic_cast 还是其他原因。

【讨论】:

【参考方案8】:

我不确定取消引用 NULL 的问题,但我可以解释代码的行为。

dynamic_cast(p)->special();

打印:“特殊投资功能”

cout (p)

prints: "0xABABABA"

就像跑步:

cout

【讨论】:

以上是关于“dynamic_cast”之后的 NULL 指针实际上可以被取消引用吗?的主要内容,如果未能解决你的问题,请参考以下文章

为什么要使用dynamic_cast

为什么要使用dynamic_cast

dynamic_cast

在没有dynamic_cast的大层次结构中确定基指针的真实类型

使用指向基础抽象类的指针访问子类成员,该基础抽象类不能是 dynamic_cast

C++:即使转换的对象不是 NULL,dynamic_cast 也会导致 SEGFAULT。怎么会这样?