- > null对象上的运算符

时间:2012-03-16 13:51:02

标签: c++

根据C ++标准,完全可以接受:

class P
{
    void Method() {}
};

...

P* p = NULL;
p->Method();

然而,稍微改变一下:

class P
{
    virtual void Method() {}
};

...

P* p = NULL;
p->Method();
使用Visual Studio 2005编译时,

会产生访问冲突。

据我了解,这是由微软编译器实现中的一些怪癖引起的,而不是由于我完全没有能力进行更改,所以问题是:

1)此行为是否会在更新版本的VS中持续存在?

2)是否存在任何阻止此访问冲突的编译器设置?

3 个答案:

答案 0 :(得分:11)

根据C ++标准,完全可以接受

不,不是!
根据C ++标准,取消引用NULL指针是未定义行为 [#1]

但是,如果您不访问非虚拟成员函数中的任何成员,则它很可能适用于每个实现,因为对于非虚拟成员函数,this只需要为了访问{{{}成员而不需要1}}因为在函数内部没有成员被访问,因此结果 但是,仅仅因为可观察的行为是可以的并不意味着程序格式良好。正确。
它仍然是不正确的。
不过,这是一个无效的程序。

第二个版本崩溃是因为在访问虚拟成员函数时,即使在该成员函数中没有访问成员,即使调用适当的成员函数,也需要取消引用this指针。

好读:
What's the difference between how virtual and non-virtual member functions are called?


[#1] 参考:

C ++ 03标准:§1.9/ 4

  

本国际标准中将某些其他操作描述为未定义(例如,取消引用this指针的效果)。 [注意:本国际标准对包含未定义行为的程序的行为没有要求。 ]

答案 1 :(得分:3)

正如AI所说......我甚至会解释原因:在许多C ++实现中,this指针只是作为方法的第一个“隐藏”参数传递。所以你看到的是

void Method() {}

真的是

void Method(P* this) {}

但对于虚拟方法来说,它更复杂。运行时需要访问指针以找到“真实”类型的P *,以便能够调用该方法的“正确”虚拟实现。所以它就像

p->virtualTable->Method(p);

所以总是使用p。

答案 2 :(得分:0)

首先,任何人都不会编译,因为你已将Method定义为私有。

假设您公开了Method,那么在这两种情况下都会导致未定义的行为。基于典型的实现,大多数编译器将允许第一个“工作”(对于相当宽松的工作定义),而第二个将基本上总是失败。

这是因为非虚拟成员函数基本上是接收额外参数的普通函数。在该函数内部,关键字this引用该额外参数,该参数是指向调用该函数的类实例的指针。如果通过空指针调用成员函数,则主要意味着函数this内部将是空指针。只要该函数中没有任何内容试图取消引用this,您可能会看到任何明显的副作用。

然而,虚函数基本上是通过指针调用的函数。在典型的实现中,任何具有一个或多个虚函数的类(无论是直接在该类中定义,还是从基类继承)都将具有vtable。该类的每个实例(即每个对象)将包含指向其类的vtable的指针。当您尝试通过指针调用虚函数时,编译器将生成以下代码:

  1. 取消引用指针。
  2. 从该对象中的适当偏移量中获取vtable指针
  3. 取消引用vtable指针以获取类'vtable
  4. 查看vtable中的正确偏移量以获取指向要调用的函数的指针
  5. 调用该函数
  6. 给定一个空指针,该进程的第一步将会中断。

    我注意到这个记录,这适用于几乎所有 C ++编译器。 VC ++在这方面远非独一无二。恰恰相反 - 虽然理论上编译器实现虚拟函数(例如)可能与此不同,但实际情况是,我所知道的每个编译器对于您发布的代码类型都基本相同。实际上,所有C ++编译器在相同的代码下都会显示相似的行为 - 实现中的主要差异主要是理论上的可能性,而不是实际上可能遇到的那些。