【问题标题】:Why does this code only print 42?为什么这段代码只打印 42?
【发布时间】:2009-07-18 19:11:04
【问题描述】:

谁能解释一下为什么这段代码只打印“42”而不是“created\n42”?

#include <iostream>
#include <string>
#include <memory>

using namespace std;

class MyClass
{
public:
    MyClass() {cout<<"created"<<endl;};
    int solution() {return 42;}
    virtual ~MyClass() {};
};

int main(int argc, char *argv[])
{
    auto_ptr<MyClass> ptr;
    cout<<ptr->solution()<<endl;
    return 0;
}

顺便说一句,我在解决方案中用不同的值尝试了这段代码,我总是得到“正确”的值,所以它似乎不是一个随机的幸运值。

【问题讨论】:

  • 我的建议是查阅“银河系漫游指南”。

标签: c++ gcc auto-ptr


【解决方案1】:

因为它表现出未定义的行为 - 您取消引用空指针。

当你说:

 auto_ptr<MyClass> ptr;

你创建了一个不指向任何东西的自动指针。这相当于说:

MyClass * ptr = NULL;

那么当你说:

cout<<ptr->solution()<<endl;

你取消引用这个空指针。这样做在 C++ 中是未定义的 - 对于您的实现,它似乎可以工作。

【讨论】:

  • 哦,我不是在抱怨 :) 我提交后提高了你的水平,我抽奖很慢。
  • 严肃地说,我希望提问者在接受答案之前稍等片刻。我可以建议至少等待 12 小时吗?
  • 我不会说这是幸运的——因为solution() 实际上并没有使用this 指针(它没有访问任何成员变量或虚函数),所以它没有使用也就不足为奇了。不会崩溃。
  • 好吧,我实际上将我的首选答案更改为这个答案,因为它清楚地表明未定义的行为意味着由您的编译器特定的 c++ 实现来选择要做什么,这回答了我关于为什么的问题它正在打印一些价值:)。很抱歉这么频繁地更改我的选择,它不会再发生了。谢谢大家的回答,你摇滚!
  • @Adam 实际上,我有点(但不完全)惊讶它没有崩溃。但这就是 UB 的奇妙之处。
【解决方案2】:

std::auto_ptr 不会自动为您创建对象。也就是说,main 中的ptr 被初始化为null。取消引用这是未定义的行为,您只是碰巧幸运并因此获得 42。

如果你真的创建了对象:

int main(int argc, char *argv[])
{
    auto_ptr<MyClass> ptr(new MyClass);

    cout << ptr->solution() << endl;

    return 0;
}

你会得到你期望的输出。

【讨论】:

  • 我一直都知道 42 是一切的答案,但没想到空指针也知道这一点。谢谢!顺便说一句,为什么这不会引发分段错误?
  • 因为这是未定义行为的作用,它可能看起来有效,或者它可能会重新格式化您的计算机。可能它起作用的原因是因为您实际上并未对班级成员进行操作。编译器看到您正在访问MyClass::solution。它为 this 指针输入 0,因为它就是这样,进入函数,得到 42 作为结果,然后返回。为您的 clsas 提供一个私有成员 int answer,在构造函数中将其设置为 42,然后在 solution() 中返回它,您应该会看到崩溃,因为现在您实际上是在尝试使用 null this 指针。跨度>
  • @rlazo:它不会引发段错误,因为您的解决方案函数不访问任何成员变量,因此函数内部未使用“this”指针。
  • 它不会引发分段错误,因为 solution() 不是虚拟的并且不访问任何成员变量。 (不是说你应该指望这个。)
  • 它“不应该”:) 只是 C++ 编译器通常工作方式的副作用。
【解决方案3】:

首先,请记住auto_ptr 的-&gt; 运算符本质上是转发到包含的指针。因此,对于本次讨论,main 中的代码等同于:

MyClass* ptr = NULL;
cout << ptr->solution() << endl;

然后请注意,编译器倾向于以非常类似于将this 指针作为另一个函数参数传递的非成员函数的方式来实现成员函数。因此,从您当前编译器的角度来看,main 中的代码就像是:

MyClass* ptr = NULL;
cout << solution(ptr) << endl;

解决方案写成:

int solution(MyClass* this) { return 42; }

在这种情况下,为什么没有崩溃就很明显了。


然而,正如其他人已经提到的,这些是编译器如何实现 C++ 的内部细节,语言标准没有指定这些细节。所以理论上这段代码可以在一个编译器上按照这里描述的那样工作,但在另一个编译器上完全崩溃或做其他事情。

但在实践中,即使标准不保证这种行为,任何特定的编译器都可以保证如果他们愿意。例如:由于 MFC 依赖于这种行为,Visual Studio 不太可能停止支持它。当然,您必须研究可能使用您的代码的每个特定编译器,以确保它们确实保证了这种行为。

【讨论】:

    【解决方案4】:

    因为你不知道答案xD的问题

    看来你没有调用构造函数,对吧?

    【讨论】:

      【解决方案5】:

      您不是在创建对象的实例。
      您只是在创建一个智能指针。

      当您调用该方法时,您正在取消引用 NULL 指针,因此正如 Neil 所说,您现在处于未定义的行为中。但是由于您的代码不会尝试访问任何成员变量,因此幸运的是它不会崩溃。

      试试这个:

      auto_ptr<MyClass> ptr(new MyClass);
      

      【讨论】:

      • 我会说“不幸”而不是“幸运”。
      【解决方案6】:

      因为ptr 未初始化,你很幸运。你应该先打电话给new:

      auto_ptr<MyClass> ptr( new MyClass );
      

      【讨论】:

        【解决方案7】:

        您不会遇到崩溃,因为“解决方案”方法不需要实际使用类成员。如果您要返回会员或其他东西,您可能会遇到崩溃。

        【讨论】:

          猜你喜欢
          • 2017-05-08
          • 1970-01-01
          • 2023-03-25
          • 1970-01-01
          • 2022-12-18
          • 2021-05-24
          • 2019-11-17
          • 1970-01-01
          • 2012-03-23
          相关资源
          最近更新 更多