简单的代码,看似随机的结果 - 这是由于过时的引用吗?

Posted

技术标签:

【中文标题】简单的代码,看似随机的结果 - 这是由于过时的引用吗?【英文标题】:simple code, seemingly random result - is this due to a stale reference? 【发布时间】:2016-04-08 01:41:02 【问题描述】:

下面的代码涉及一个由 int 向量组成的向量,它由一个带有两个元素的向量组成。该代码非常不言自明。我希望每个打印语句的输出为 2,但第二个打印语句打印 6。

我猜这与引用的传递和事物的构造顺序有关,但我无法解释。你能解释一下这个输出吗?我究竟做错了什么?

谢谢,

#include <vector>
#include <iostream>
#include <cassert>

using namespace std;

class Permutation

    public:

    Permutation(const vector<vector<int> >& cycles) : _cycles(cycles)
    

    static Permutation foo()
    
        vector<vector<int> > cycles = 2,3;

        assert(cycles.front().size() == 2); // foo seems to create the correct thing locally

        return Permutation(cycles);
    

    const vector<vector<int> >& _cycles;
;


int main()
    
    vector<vector<int> > cycles = 2,3;
    Permutation perm(cycles);
    cout << perm._cycles.front().size() << endl; // outputs 2, as expected

    cout << Permutation::foo()._cycles.front().size() << endl; // outputs 6, seems like garbage to me

    return 0;

【问题讨论】:

_cyclesfoo 中持有对cycles 的引用,一旦foo 返回,该引用就失效了。 【参考方案1】:

您的猜测大部分是正确的。并不是引用的传递是个问题,而是在所有的引用都被到处传递之后,被引用的对象超出了范围并被销毁了,而引用仍然悬而未决。在那个时候使用引用变成了未定义的行为。

当一切都说完了,_cycles 最终成为来自已返回函数的函数范围对象的引用,从而破坏了被引用的对象。您所做操作的简化示例:

int &foo()

    int bar=4;

    return bar;


void foobar()

    int &blah=foo();

foo() 返回了一个函数范围对象的引用,该对象在foo() 返回时已经被销毁,因此返回的引用是指一个超出范围并被销毁的对象。使用引用现在是未定义的行为。

【讨论】:

【参考方案2】:

在 foo() 中,您在当前堆栈帧中分配一个 Permutation 对象,然后返回给调用者,销毁该帧(即超出范围)。相反,您可以在堆上分配新的 Permutation 对象并返回指针,请记住,如果在除立即退出的示例之外的其他操作中完成,您将希望在某个时候释放它。

#include <vector>
#include <iostream>
#include <cassert>

using namespace std;

class Permutation

    public:

    Permutation(const vector<vector<int> >& cycles) : _cycles(cycles)
    

    static Permutation * foo()
    
        vector<vector<int> > cycles = 2,3;

        assert(cycles.front().size() == 2); // foo seems to create the correct thing locally

        return new Permutation(cycles);
     

    const vector<vector<int> >& _cycles;
;


int main()
    
    vector<vector<int> > cycles = 2,3;
    Permutation perm(cycles);
    cout << perm._cycles.front().size() << endl; // outputs 2, as expected

    cout << Permutation::foo()->_cycles.front().size() << endl; // outputs 6, seems like garbage to me

    return 0;

【讨论】:

【参考方案3】:

我换行了:

常量向量>& _cycles;

常量向量> _cycles;

一切正常!

【讨论】:

那是因为,您的代码现在不再包含对可能被销毁的对象的引用,而是包含该对象的副本,该副本将在原始对象被销毁后继续存在(并链接到生命周期Permutations 对象)。如果您不明白为什么会这样,请避免使用作为引用的类/结构成员。

以上是关于简单的代码,看似随机的结果 - 这是由于过时的引用吗?的主要内容,如果未能解决你的问题,请参考以下文章

由于C#中ADO.NET对Oracle的命名空间引用时提示过时,为此想用Linq对数据库的连接等操作(见补充)

如何从mssql随机取出一条记录,看似很简单的问题

批处理文件在 for-Loop 开始时看似随机崩溃

MySql / PHP 警报系统不只报告一个看似随机的数据

用 C++ 流式传输视频? [关闭]

返回结构指针的字符串打印随机结果[重复]