c++ - 什么时候在空实例上调用成员函数会导致未定义的行为?

标签 c++ undefined-behavior language-lawyer standards-compliance null-pointer

考虑以下代码:

#include <iostream>

struct foo
{
    // (a):
    void bar() { std::cout << "gman was here" << std::endl; }

    // (b):
    void baz() { x = 5; }

    int x;
};

int main()
{
    foo* f = 0;

    f->bar(); // (a)
    f->baz(); // (b)
}

我们预计(b)会崩溃,因为空指针没有对应的成员x。实际上,(a) 不会崩溃,因为从未使用过 this 指针。

因为 (b) 取消引用 this 指针 ((*this).x = 5;),并且 this 为 null,程序进入未定义行为,因为取消引用 null 总是被认为是未定义行为。

(a) 会导致未定义的行为吗?如果两个函数(和 x)都是静态的呢?

最佳答案

(a)(b) 都会导致未定义的行为。通过空指针调用成员函数总是未定义的行为。如果函数是静态的,它在技术上也是未定义的,但存在一些争议。


首先要理解的是为什么取消引用空指针是未定义的行为。在 C++03 中,这里实际上有一点歧义。

尽管在 §1.9/4 和 §8.3.2/4 的注释中都提到了 “取消引用空指针会导致未定义的行为”,但从未明确说明。 (注释不规范。)

但是,可以尝试从 §3.10/2 中推导出它:

An lvalue refers to an object or function.

当取消引用时,结果是一个左值。空指针引用对象,因此当我们使用左值时,我们有未定义的行为。问题是上一句从来没有陈述过,那么“使用”左值是什么意思呢?甚至只是生成它,还是在执行左值到右值转换的更正式意义上使用它?

无论如何,它绝对不能转换为右值 (§4.1/1):

If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.

这绝对是未定义的行为。

歧义来自于它是否是未定义的行为来尊重但不使用来自无效指针的值(即,获取左值但不将其转换为右值)。如果不是,则 int *i = 0; *我; &(*i); 定义明确。这是 active issue .

所以我们有一个严格的“解引用一个空指针,得到未定义的行为” View 和一个弱的“使用一个解引用的空指针,得到未定义的行为” View 。

现在我们考虑这个问题。


是的,(a) 会导致未定义的行为。事实上,如果 this 为空,则无论函数的内容如何结果都是未定义的。

这来自 §5.2.5/3:

If E1 has the type “pointer to class X,” then the expression E1->E2 is converted to the equivalent form (*(E1)).E2;

*(E1) 将导致具有严格解释的未定义行为,.E2 将其转换为右值,使其成为弱解释的未定义行为。

还可以看出它是直接来自 (§9.3.1/1) 的未定义行为:

If a nonstatic member function of a class X is called for an object that is not of type X, or of a type derived from X, the behavior is undefined.


对于静态函数,严格解释与弱解释是不同的。严格来说,它是未定义的:

A static member may be referred to using the class member access syntax, in which case the object-expression is evaluated.

也就是说,它的计算就像它是非静态的一样,我们再次使用 (*(E1)).E2 取消引用空指针。

但是,因为 E1 没有在静态成员函数调用中使用,所以如果我们使用弱解释,则调用是明确定义的。 *(E1) 产生左值,静态函数被解析,*(E1) 被丢弃,函数被调用。没有左值到右值的转换,因此没有未定义的行为。

在 C++0x 中,从 n3126 开始,歧义仍然存在。现在,安全一点:使用严格的解释。

关于c++ - 什么时候在空实例上调用成员函数会导致未定义的行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31075819/

相关文章:

c++ - 对象 vector - 如何向构造函数发送参数?

c++ - 了解 unique_ptr get() 函数

c++ - 需要有关使用 Visual C++ 创建图形绘图的建议

c++ - 是否需要显式构造 char 数组的每个成员?

c++ - 比较从字符串转换的浮点值与文字

c++ - std::bit_cast 生成多个值的值表示的示例是什么?

c++ - 为什么在 C++ 中运行时才知道动态类型?

c++ - 阈值绝对值

c++ - 严格别名和对编译时 C 数组的引用

c++ - 正在将 size_t(大于 int 范围)分配给 int UB?