2011-09-29 38 views
8

我认为这个问题最好用一小段代码我只是写问:当我通过引用传递一个对象并且超出了范围时,C++中会发生什么?

#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()时,我期待着某种失败。我是否理解传递/存储引用的工作方式是不正确的,还是这种情况只是小规模工作,并可能在更复杂的应用程序中爆炸?

+3

难道你不喜欢它,当我们都喊“未定义的行为!”一致? 8v) –

+0

“我期待第二次出现某种失败”。只需使print()虚拟或将要打印的文本存储为std :: string成员变量,就会失败。 – SigTerm

+1

您通常不希望引用为C++中的数据成员。您可能来自Java背景。 C++引用与Java引用完全不同。如果你想要一个像Java引用的东西,可以使用'std :: shared_ptr '并在堆上创建对象。 – fredoverflow

回答

11

它的工作原理与指针一样:使用引用不再存在的对象的东西(指针/引用)是未定义的行为。它似乎可以工作,但可以随时中断。

警告:接下来的内容快速解释了为什么此类方法调用似乎可以在多个场合工作,仅供参考;编写实际的代码时,你应该只依靠什么标准说

至于你所观察的行为:大多数(全部?)编译器方法调用实现的功能有隐藏this参数指的是调用该方法将运行的类的实例。但在你的情况下,this指针没有被使用(函数中的代码不是指任何字段,也没有虚拟调度),所以(现在无效)this指针未被使用,并且调用成功。

在其他情况下,即使它指向超出范围的对象,因为它的内存还没有被重用(尽管析构函数已经运行,所以该方法可能会找到对象不一致的状态)。

同样,你不应该依赖这些信息,只是让你知道为什么这个调用仍然有效。

1

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

我实际上修复了一次工作中的程序中的相同错误。当使用Intel的编译器时,超出范围的变量还没有被“清理”,所以内存仍然“有效”(但行为未定义)。然而,微软的编译器更积极地清理它,并且错误是显而易见的。

12

当您存储对已终止其生命周期的对象的引用时,访问它是未定义的行为。因此,任何事情都可能发生,它可以工作,可能会失败,它可能会崩溃,并且因为看起来我喜欢说它可以订购比萨饼。

+1

它可以支付比萨吗? ;) –

+1

@ R.MartinhoFernandes:这将是符合标准的行为! –

+1

@ R.马丁尼费尔南德斯:是的,但不一定。由于其未定义的行为,你无法继承。 –

1

您有一个悬挂参考,导致未定义的行为。

相关问题