为什么在删除指针上调用非虚拟成员函数是未定义的行为?

时间:2012-12-23 16:11:41

标签: c++ undefined-behavior language-lawyer dereference

正如标题所说:

为什么在已删除指针上调用非虚拟成员函数是未定义的行为?

注意问题不会询问它是否是未定义的行为,它会询问 为什么 它是未定义的行为。


考虑 following program

#include<iostream>
class Myclass
{
    //int i
    public:
      void doSomething()
      {
          std::cout<<"Inside doSomething";
          //i = 10;
      }
};

int main()
{
    Myclass *ptr = new Myclass;
    delete ptr;

    ptr->doSomething();

    return 0;
}

在上面的代码中,编译器在调用成员函数this时实际上并不解除引用doSomething()。请注意,该功能不是虚拟功能&amp;编译器将成员函数调用转换为通常的函数调用,将其作为第一个参数传递给函数(据我所知,这是实现定义的)。他们可以这样做,因为编译器可以准确地确定在编译时调用哪个函数。实际上,通过删除指针调用成员函数不会取消引用this。只有在函数体内访问任何成员时才会取消引用this。(即:取消注释以上示例中访问i的代码)
如果在函数中没有访问成员,则上述代码实际上不应该实际调用未定义的行为。

那么为什么通过删除指针调用非虚拟成员函数的标准命令是未定义的行为,实际上它可以可靠地说解除引用this应该是应该导致未定义行为的语句?仅仅为了语言用户的简单起见,标准简单地概括了它还是在这个任务中涉及更深层次的语义?

我的感觉是,因为它是实现定义的,编译器如何调用成员函数可能是标准不能强制执行UB的实际点的原因。

有人可以确认吗?

4 个答案:

答案 0 :(得分:9)

因为它可能是可靠的案件数量如此之小,并且这样做仍然是一个不可思议的愚蠢想法。定义行为没有任何好处。

答案 1 :(得分:6)

  

那么为什么通过删除指针调用非虚拟成员函数的标准命令是未定义的行为,实际上它可以可靠地说解除引用它应该是应该导致未定义行为的语句?

[expr.ref]第2段说成员函数调用如ptr->doSomething()等同于(*ptr).doSomething()所以调用非静态成员函数 是取消引用。如果指针无效,则表示未定义的行为。

生成的代码是否实际需要取消引用特定情况的指针是不相关的,编译器模拟 的抽象机器原则上会进行解除引用。

使语言复杂化以准确定义哪些案例将被允许,只要他们不访问任何成员将几乎没有任何好处。在您无法看到函数定义的情况下,您不知道调用它是否安全,因为您无法知道该函数是否使用this

只是不要这样做,没有充分的理由,语言禁止它是一件好事。

答案 2 :(得分:5)

在C ++语言中(根据C ++ 03),使用无效指针值的尝试已经导致了未定义的行为。 UB没有必要取消引用它。只需读取指针值即可。当您仅仅尝试读取该值时,导致UB的“无效值”概念实际上扩展到几乎所有标量类型,而不仅仅是指针。

delete之后,指针在某种特定意义上通常是无效的,即读取一个可能指向刚被“删除”的指针会导致未定义的行为。

int *p = new int();
delete p;
int *p1 = p; // <- undefined behavior

通过无效指针调用成员函数只是上述的一个特例。指针用作隐式参数this的参数。传递指针是非引用参数是读取它的行为,这就是为什么行为在您的示例中未定义。

所以,你的问题实际上归结为为什么读取无效指针值会导致未定义的行为。

嗯,可能有许多特定于平台的原因。例如,在某些平台上,读取指针的行为可能会导致指针值被加载到某个专用的地址专用寄存器中。如果指针无效,硬件/ OS可能会立即检测到它并触发程序错误。实际上,这就是我们流行的x86平台在段寄存器方面的工作方式。我们没有听到太多关于它的唯一原因是流行的操作系统坚持平板存储器模型,它不会主动使用段寄存器。


C ++ 11实际上声明解除引用无效指针值导致未定义行为,而无效指针值的所有其他使用导致实现定义行为。它还指出,在“复制无效指针”的情况下,实现定义的行为可能导致“系统生成的运行时故障”。因此,实际上可以通过C ++ 11规范的迷宫仔细操作,并成功地得出结论,通过无效指针调用非虚方法应该导致实现定义上面提到的行为。无论如何,“系统生成的运行时故障”的可能性总是存在。

答案 3 :(得分:2)

在这种情况下取​​消引用this实际上是一个实现细节。我不是说this指针不是由标准定义的,因为它是,但从语义上抽象的观点来看,允许使用已被破坏的对象的目的是什么,仅仅因为有一个在实际中它将是“安全”的角落案例?没有。所以事实并非如此。没有对象存在,因此您可能无法调用它上面的函数。