I think this question is best asked with a small code snippet I just wrote:
#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;
}
The code compiles and runs fine using g++:
$ g++ test.cc -o test
$ ./test
Printing my friend: I'm printing
Printing my friend: I'm printing
However, this is not the behavior I was expecting. I was expecting some sort of failure on the second call to fc->printFriend(). Is my understanding of how the passing/storing by reference works incorrect or is this something that just happens to work on a small scale and would likely blow up in a more sophisticated application?
It works exactly as for pointers: using something (pointer/reference) that refers to an object that no longer exists is undefined behavior. It may appear to work but it can break at any time.
Warning: what follows is a quick explanation of why such method calls can seem to work in several occasions, just for informative purposes; when writing actual code you should rely only on what the standard says
As for the behavior you are observing: on most (all?) compilers method calls are implemented as function calls with a hidden
thisparameter that refers to the instance of the class on which the method is going to operate. But in your case, thethispointer isn’t being used at all (the code in the function is not referring to any field, and there’s no virtual dispatch), so the (now invalid)thispointer is not used and the call succeeds.In other instances it may appear to work even if it’s referring to an out-of-scope object because its memory hasn’t been reused yet (although the destructor has already run, so the method will probably find the object in an inconsistent state).
Again, you shouldn’t rely on this information, it’s just to let you know why that call still works.