3

While searching for some loosely related stuff I bumped into this quote:

And a reference can outlive an object and be used to refer to a new object created at the same address.

From this answer.

Now, I've always known and worked by references being immutable, initialized once and all that. Reading the above quote, by someone likely more experienced than I am, got me wondering if I'm missing something.

Was that sentence meant to be for the sake of completeness but practically inapplicable?

Is there some pattern or circumstance where people would go through the pain of landing a new object of the same type in a specific memory address just to to do a switcheroo for a reference? (which seems supremely dangerous to me, not to mention convoluted at the best of times).

Community
  • 1
  • 1
  • I'm not sure about "practically inapplicable", but I bet it's one of those one-in-a-million things. 99.999999% of programmers will never do it – Mooing Duck Jan 30 '15 at 21:53
  • The thread is talking about references to pointers. I imagine that context is valuable in this instance. –  Jan 30 '15 at 21:55
  • 2
    I'm inclined to think it's useless, or even dangerous, in practical circumstances. It's probably just in there because compilers can't enforce references to die with their objects. – Suedocode Jan 30 '15 at 22:01
  • @remyabel the answer seemed more generic than that to me, which is why it got me wondering (and I still am) if there's a nifty trick somewhere I'm unaware of. It happened before (to me). –  Jan 30 '15 at 22:08

4 Answers4

3

The problem of dangling references is essentially the same as the problem of dangling pointers.

For example, two functions

int &GetReference()
{
    int x;    //  local variable

    return x;
}

int *GetPointer()
{
    int x;

    return &x;
} 

cause exactly the same problems for the caller if the returned reference is used, or the pointer dereferenced.

int &r = GetReference();
int *p = GetPointer();

r = 52;
*p = 42;

Both of the assignments exhibit undefined behaviour, since the variables named x (within the two functions) no longer exist, as far as the program is concerned. However, the code can SEEM to work correctly.

The same can happen with creating dangling references or pointers by releasing dynamically allocated memory (free() in C, operator delete in C++).

If other code (eventually) uses that memory (e.g. to represent another variable, to represent an unrelated object), that reference or pointer does often have access to whatever is at that memory location. That can give spurious problems of the value changing (which can give surprises for code using the reference, or for the unrelated code that finds variables or objects being changed).

It is not something to aspire to, or to use, practically. It is a dangerous program flaw that is often very hard to fix or debug - because it provides a path for two completely unrelated sections of code to affect data used by the other.

Fortunately, modern compilers usually (if configured to give maximum warning levels) do give warnings about a lot of suspicious constructs (e.g. returning a pointer or reference to a local variable).

Rob
  • 1,966
  • 9
  • 13
  • "Fortunately, modern compilers **usually** do give warnings about a lot of suspicious constructs." My many years of segfaults say otherwise. – Suedocode Jan 30 '15 at 22:35
  • Compilers are rarely configured by default to give such warnings, for various historical reasons, such as lazy programmers lobbying. Almost modern compilers, however, can be configured to give such warnings. There are, unfortunately, also circumstances that a compiler cannot detect. – Rob Jan 30 '15 at 22:37
  • I always compile with `-Wall`. I'm not faulting compilers for it, but I would advise against relying on its checks too much. There's only so much compile time information that it can use, and anything dealing with pointer addresses is *very* limited outside of run-time. – Suedocode Jan 30 '15 at 22:45
  • 1
    The quoted statement is paraphrasing a much more complex rule in the Standard, which adds the additional requirement of "before the storage is reused", which has the effect of ruling out the usage you are discussing. Everything you said is correct... but irrelevant to the particular guarantee which was mentioned in the other answer. – Ben Voigt Jan 30 '15 at 23:03
  • Sure. However, the compiler checks (if warnings are heeded) can help avoid a lot of errors. But you're right that there are circumstances where a compiler can't help. A lot of techniques and idioms by various authors (Sutter, Myers, several others) are about preventing such problems. Techniques based on RAII, for example. – Rob Jan 30 '15 at 23:05
3

I think this could only make sense in the context of a placement new. If the object the reference points to was created with placement new, it should be possible to destroy the object and create a new object in the same spot with placement new again. I don't see any immediate reason not to use a pointer instead at the moment though.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
midor
  • 5,487
  • 2
  • 23
  • 52
  • 2
    Indeed, this statement was paraphrasing the Standard sections on storage lifetime and object lifetime and reuse by calling placement new on the same memory area is a major focus. No delete though, delete is for getting memory. Counterpart to placement new is an explicit destructor call. – Ben Voigt Jan 30 '15 at 22:53
  • s/getting memory/freeing memory/... bad Swype autocorrect :( – Ben Voigt Jan 30 '15 at 23:05
2

Here's the full paragraph from the C++ Standard, which I was paraphrasing:

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:

  • the storage for the new object exactly overlays the storage location which the original object occupied, and
  • the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
  • the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and
  • the original object was a most derived object (1.8) of type T and the new object is a most derived object of type T (that is, they are not base class subobjects).

"A new object is created at the storage location" is certainly the effect of placement-new, as midor observes.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
-1

Under the covers, references are little more than auto-dereferencing pointers, so they suffer from some of the same issues as pointers. References can be stale, just like pointers can. The memory referred to by a reference can be reused by other objects, just as it can for the memory referred by by a pointer. I think that's all the statement in question is trying to say.

Would a sane programmer intentionally leverage this? Not likely, nor do I think the comment was suggesting that you attempt to do so (although technically it is possible, using placement new). I think it was more to prevent people from thinking along the lines of managed languages, where the mere existence of a reference is enough to keep an object alive.

Darryl
  • 5,907
  • 1
  • 25
  • 36
  • The misconception I'm fighting is the oft-quoted but false "fact" that a reference is bound to an object. It's not;just a like a pointer a reference is bound to a memory location. – Ben Voigt Jan 30 '15 at 23:16
  • I agree the "fact" is often quoted inappropriately, but you're going to far - it is correct in context, even if people often quote it out of context. The type of the reference specifies the meaning of what is at the associated memory location - i.e. causes valid operations (mostly in the sense of no undefined behaviour in play) which access/affect that memory to behave as if there is an object of that type at that memory location. Semantically, this means that a C++ reference is bound to an object which, in turn, is bound to a memory location. – Rob Feb 03 '15 at 05:04
  • @Rob, But the object at that memory location may not be the same one that was used to initialize the reference. Therefore, the reference is not bound to its initializing object, it is bound to its memory location. See the quote from the C++ standard in Ben's answer to this question. – Darryl Feb 03 '15 at 22:20
  • Applicability of that quote is pretty limited (as per the bullet points Ben quoted). In fighting one misconception, you're presenting something else as if it is always true, when it is only true in limited circumstances. So you're replacing one misconception with another. – Rob Feb 04 '15 at 13:54