我可以举一个现实生活中的例子,其中非静态成员函数不访问通过空指针调用的对象会导致可观察到的问题吗? [复制]

Posted

技术标签:

【中文标题】我可以举一个现实生活中的例子,其中非静态成员函数不访问通过空指针调用的对象会导致可观察到的问题吗? [复制]【英文标题】:May I have a real life example where a non-static member function not accessing the object being called via a null pointer causes observable problems? [duplicate] 【发布时间】:2012-02-29 17:43:47 【问题描述】:

可能重复:When does invoking a member function on a null instance result in undefined behavior?

这样的:

class Class 
public:
    void Method()
    
       //empty;
    
;

Class* object = 0;
object->Method();

在 C++ 中是未定义的行为,因为通过空指针调用非静态成员函数在形式上是非法的。请参阅 this answer 以获得完整的 C++ 标准引用的详细解释。我很清楚理论部分,这个问题不是关于理论的,所以它不是那个问题的重复。

在所有实现中,我知道上面的代码或其等效代码不会导致任何可观察到的问题 - 因为成员函数不访问对象,所以该方法将被正常调用。

我可以举一些现实生活中的例子,其中相同的设置会导致实际的可观察到的问题吗?

【问题讨论】:

如果你把一些不访问对象数据成员的琐碎代码放在里面会发生什么?就像 g_Num = rand();其中 g_Num 是一个全局变量。 @KennyTM:不是骗子。该问题询问“何时是 UB”,答案是“始终是 UB,因为标准是这样说的”。我知道标准是怎么说的,并且想知道一个导致可观察到的问题的例子。 @ksming:通常会执行该代码。 显然,如果函数是虚拟的,它惨败。 如果我闭着眼睛过马路,会不会总是被公交车碾过? 【参考方案1】:

简单:

struct Object  void foo(); std::string s; ;

void print(Object* o) 
  o->foo();
  if (o)  std::cout << o->x << "\n"; 

假设foo 不访问Object 的任何非静态属性(即x)。

问题在于,如果o 为空,正式o-&gt;foo() 是未定义的行为,那么o 不为空是明显。因此检查是多余的。

功能因此得到优化:

void print(Object* o) 
  o->foo();
  std::cout << o->x << "\n";

颠倒顺序不会改变任何事情:

void print(Object* o) 
  if (o)  std::cout << o->x << "\n"; 
  o->foo();

仍在优化:

void print(Object* o) 
  std::cout << o->x << "\n";
  o->foo();

有时被某些 SO 成员称为未定义行为的时间旅行条款。

有关更多信息,请查看 Chris Lattner 关于未定义行为的系列:

What Every C Programmar Should Know About Undefined Behavior 1/3 What Every C Programmar Should Know About Undefined Behavior 2/3 What Every C Programmar Should Know About Undefined Behavior 3/3

2/3 解决了您的具体问题。

这是否真的失败取决于您使用的编译器、您指定的优化过程以及它们的运行顺序。

真的想依赖这一切吗:x?

当然,有人会争辩说,拥有一个不访问对象任何状态的函数成员是没有意义的......所以这个问题本身在实践中没有什么价值(但对于它的理论方面很有趣)。

【讨论】:

太棒了,这看起来就像我要找的一样。 顺便说一句,“时间旅行条款”到底是什么?我可以参考任何此类用法吗? @sharptooth: 没有 reference AFAIK,我刚刚看到这里的一些成员(也许是 GMan 或 jalf)使用它几次。如果您查看第二个示例(其中测试在o-&gt;foo() 语句之前),您可以看到o-&gt;foo() 的存在会影响测试(已被删除),即使测试(在正常情况下)之前执行过。因此,o-&gt;foo() 的 UB 在执行o-&gt;foo() 语句之前 造成崩溃,就好像它穿越到过去一样:) 仅供参考:我发现了一个不包括核心优化的更真实的例子:***.com/a/3257755/57428 你的也很棒。 @sharptooth:确实很有趣,但由于实际访问了this,因此略有不同。有趣的是,允许编译器考虑 this 永远不是 null,因此即使没有多个基数,该示例也可能中断:)【参考方案2】:

这是 UB,那么什么会构成“问题”?要成为一个问题,代码必须做一些标准规定它应该做或我们期望它做的事情之外的事情,否则它不是问题。标准让我们不知道代码应该或将要做什么,所以无论做什么都不是问题。

您说它不会导致任何“可观察到的问题”。好吧,当然不是。不管做什么都会好的。它可能会出错,这不会是一个“问题”,因为这是标准告诉我们可能发生的事情。

【讨论】:

【参考方案3】:

这段代码不太可能在我知道的任何平台上产生任何问题。它仍然是未定义的行为,主要是因为它是一个不相关的极端情况:可以定义在某些条件下调用空指针上的成员函数是可以的。然而,有什么意义呢?如果您不访问任何成员,为什么要将该函数设为非静态成员函数?不定义这种情况并没有什么损失,因为没有必要定义这种情况,因此会不必要地使语言复杂化。

【讨论】:

你可以在类中有一个dump() 函数来检查this == 0,然后打印“Null”或一些诊断。如果你有一堆指针,一些是空的,一些是有效的,你可以在所有这些上盲目地调用dump()。但是,由于 Matthieu M. 指出的原因,这仍然是一个坏主意……

以上是关于我可以举一个现实生活中的例子,其中非静态成员函数不访问通过空指针调用的对象会导致可观察到的问题吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

将线程函数声明为静态函数的问题

关于纯抽象类中的静态成员函数 - 设计模式?

浅谈静态成员

浅谈静态成员

关于静态属性和静态函数

C++面向对象:C++ 数据抽象