当我通过引用传递一个对象并且它超出作用域时,在C++中会发生什么?

12

我认为最好用我刚写的一个小代码片段来问这个问题:

#include <iostream>

using namespace std;

class BasicClass
{
public:
    BasicClass()
    {
    }
    void print()
    {
        cout << "I'm printing" << endl;
    }
};

class FriendlyClass
{
public:
    FriendlyClass(BasicClass& myFriend) :
        _myFriend(myFriend)
    {
    }
    void printFriend()
    {
        cout << "Printing my friend: ";
        _myFriend.print();
    }
private:
    BasicClass& _myFriend;
};

int main(int argv, char** argc)
{
    FriendlyClass* fc;
    {
        BasicClass bc;
        fc = new FriendlyClass(bc);
        fc->printFriend();
    }
    fc->printFriend();
    delete fc;
    return 0;
}

使用g++编译和运行代码一切正常:

$ g++ test.cc -o test
$ ./test
Printing my friend: I'm printing
Printing my friend: I'm printing

然而,这不是我所期望的行为。我期望在第二次调用fc->printFriend()时会出现某种故障。我的关于通过引用传递/存储的理解有误吗?还是说这只是在小规模应用中有效,但在更复杂的应用中可能会失败?


4
你不喜欢我们一起大喊“未定义行为”吗?8v) - Fred Larson
"我预料到第二个会有某种类型的失败。只需将print()虚拟化或将要打印的文本存储为std :: string成员变量,您就会获得失败结果。" - SigTerm
1
通常情况下,你不会想在C++中将引用作为数据成员。你可能是从Java背景过来的。C++引用与Java引用完全不同。如果你想要类似于Java引用的东西,请使用std::shared_ptr<BasicClass>并在堆上创建对象。 - fredoverflow
我已经完全转换成使用 unique_ptrshared_ptrweak_ptr 在我的代码中,我很享受没有内存泄漏的好处 :) - Corey D
5个回答

15

对于指针来说,它的工作方式与其一样:使用引用到一个已经不存在的对象是未定义行为。看起来可能正常运作,但随时可能出现问题。

警告:以下是关于此类方法调用似乎在几种情况下可以正常工作的快速说明,仅供参考;在编写实际代码时,您应仅依赖于标准规定。

至于你正在观察的行为:在大多数(全部?)编译器中,方法调用被实现为带有隐藏的this参数的函数调用,该参数引用了该方法将要操作的类实例。但在你的情况下,this指针根本没有被使用(函数中的代码没有涉及到任何字段,并且没有虚拟分派),因此(现在无效的)this指针没有被使用并且调用成功了。

在其他情况下,即使它引用了一个超出范围的对象,它也可能会表现出正常工作的状态,因为其内存尚未被重用(尽管析构函数已经运行,所以该方法可能会在对象处于不一致状态时找到它)。

再次强调,您不应依赖此信息,它只是让您知道为什么该调用仍然有效。


14

当您存储对已结束其生命周期的对象的引用时,访问该引用将产生未定义的行为。因此,任何事情都可能发生,它可能工作,也可能失败、崩溃,就如同我喜欢说的那样:它可以点披萨。


1
@R.MartinhoFernandes:那将是符合标准的行为! - Fred Larson
1
@R. Martinho Fernandes:是的,但它不必如此。你不能依赖它,因为这是未定义的行为。 - K-ballo
4
就我所知,任何未定义行为都从未让我得到过披萨。我只是想声明这一点以作记录。 - Kerrek SB

1

未定义行为。根据定义,您不能对代码运行时会发生什么做出假设。编译器可能尚未清除bc所在的内存,但您不能指望它。

我曾经在工作中修复过同样的程序错误。使用英特尔的编译器时,已经超出范围的变量尚未被“清理”,因此内存仍然是“有效”的(但行为未定义)。然而,微软的编译器更积极地清理它,因此错误很明显。


1

您有一个悬空引用,这会导致未定义的行为。


1
  1. 看这里:局部变量的内存是否可以在其作用域外访问?

  2. 它几乎可以工作,因为您没有虚函数,并且您不访问BasicClass的字段:您调用的所有方法都具有静态绑定,并且从未使用“this”,因此您实际上从未访问“未分配的内存”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接