调用不通过空指针访问成员的非静态方法是否合法/定义良好的 C++?

2021-12-13 00:00:00 pointers null methods standards c++

我最近遇到了以下代码:

class Foo{上市:空栏();//.. 其他的东西};void Foo::bar(){如果这) {//.. 在不访问任何数据成员的情况下做一些事情返回;}//.. 使用数据成员执行正常操作}

代码可以编译,因为在 C++ 中,方法只是隐式传递一个指向 'this' 的指针的函数,并且可以像任何其他指针一样检查 'this' 是否为 NULL.显然,即使它没有崩溃,这段代码也是令人困惑和糟糕的做法;在调试器中单步执行代码会非常混乱,看到一个 NULL 指针即将调用一个方法,然后看不到预期的崩溃.我的问题是:调用 SomeFooPtr->bar() where SomeFooPtr == NULL 是否违反 C++ 标准?

我突然想到它可能不是因为用户定义的运算符-> 返回一个指针,这意味着即使该指针为 NULL 它也绝对没有被取消引用(我确定取消引用一个 NULL 指针被认为是标准为非法或未定义).另一方面,原始指针的语义不必与用户定义的指针的语义相匹配――也许 operator-> 对它们的引用被认为是取消引用,即使编译器不会生成一个.

解决方案

这可能适用于大多数系统,但它是未定义行为.引用标准:

<块引用>

5.2.5.3

如果 E1 的类型为指向 X 类的指针",则表达式 E1->E2 将转换为等效形式 (*(E1)).E2 [...]

还有:

<块引用>

5.2.5.1

后缀表达式后跟点 . 或箭头 ->,可选后跟关键字 template (14.8.1),然后跟一个id-expression,是一个后缀表达式.计算点或箭头前的后缀表达式;58) [...]

58) 即使结果不需要确定整个后缀表达式的值,例如如果 id 表达式表示静态成员,也会发生这种评估.

*x 的评估,其中 x 是一个空指针会导致未定义行为,所以在输入函数之前,你的显然是 UB 的情况.>

I came across the following code recently:

class Foo
{
public:
    void bar();
    // .. other stuff
};

void Foo::bar()
{
    if(!this) {
        // .. do some stuff without accessing any data members
        return;
    }

    // .. do normal actions using data members
}

The code compiles because in C++ methods are just functions that are implicitly passed a pointer for 'this' and 'this' can be checked to be NULL just like any other pointer. Obviously this code is confusing and bad practice even though it doesn't crash; it would be pretty confusing to step through the code in the debugger, see a NULL pointer is about to have a method called on it and then not see the expected crash. My question is: does it violate the C++ standard to call SomeFooPtr->bar() where SomeFooPtr == NULL?

It occurs to me that it may not because the user defined operator-> returns a pointer, which means that even if that pointer is NULL it definitely hasn't been dereferenced (dereferencing a NULL pointer I'm sure is regarded by the standard as illegal or undefined). On the other hand the semantics of raw pointers don't necessarily have to match the semantics of user defined pointers -- perhaps operator-> on them is considered a dereference even though the compiler won't generate one.

解决方案

This will probably work on most systems, but it is Undefined Behaviour. Quoth the Standard:

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 [...]

And:

5.2.5.1

A postfix expression followed by a dot . or an arrow ->, optionally followed by the keyword template (14.8.1), and then followed by an id-expression, is a postfix expression. The postfix expression before the dot or arrow is evaluated;58) [...]

58) This evaluation happens even if the result is unnecessary to determine the value of the entire postfix expression, for example if the id-expression denotes a static member.

Evaluation of *x where x is a null pointer results in Undefined Behaviour, so yours is clearly a case of UB, before the function is even entered.

相关文章