0

I want to understand why is it possible to return a reference to a class member variable in C++, such as in the following example:

class Foo
{
int x;
public:
int& get_pvar()
{
return x;
}};

Apparently we can access the variable x in main(), create a reference to it and then alter its contents:

Foo obj;
int& ref = obj.get_pvar();
ref = 7;

But how is this possible? x does not have global scope, neither is it a static member of the class. It is defined within the class. So, it should have local scope. So, why isn't it an error to return a reference to it and even create a reference to it in main()?

  • This is totally legal and unbelievably useful. It's the secret sauce of most of the operators. You can't `x[i]++;` without it. – user4581301 Dec 02 '18 at 07:16
  • As long as `obj` exists, so does `obj.x`, and a reference to it can be used. If `obj` ceases to exist, then `obj.x` ceases to exist, and using `ref` gives undefined behaviour. – Peter Dec 02 '18 at 10:26

3 Answers3

2

You are confusing scope and lifetime. Those are two related, but ultimately different concepts.

The scope of the name x is the class scope. You can only use the unqualified name x for the member inside members functions (and some other minutia, irrelevant here) of the class.

The lifetime of the member object named x is the same as the enclosing class object. In this case, the lifetime of obj.x is the same as obj. Since you return a reference to an object within its lifetime, everything checks out.


The reason for your confusion may stem from learning that objects with automatic storage duration, like the following:

{
    int x;
}

Have their lifetime bound to their lexical scope (they can only be named inside those curly braces, their scope). But while it's true for those objects, it is not something that holds in general. Class objects can live independently of the scope of their name (as you saw). And some objects can have a lifetime but no scope and name. Consider:

auto* p = new Foo();

The new expression creates an object, but it has no name! So there is no scope to even speak of. Here p is the name of a pointer, not of the newly created object.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Ok, I think I understand it a little better now, but what about the second part of my question: how can we create a reference of a member variable, *outside* the class ,in main() (i.e. ref in this case) and that too of a private variable? – HeWhoMustBeNamed Dec 02 '18 at 07:29
  • @MrReality - Access control, like scope, pertains to names. You can only access *names* if you are in the proper scope. But nothing stops you for giving someone in another scope a reference to an object you currently have access to. Accessibility of names isn't checked on indirect access via pointers or references. Those are disjoint. – StoryTeller - Unslander Monica Dec 02 '18 at 07:31
  • Sorry, I didn't understand you reply. Can you simplify that? I'm confused how we can create a reference to a member variable of a class outside the class, i.e. ref, which doesn't require an object and the dot operator to be used. – HeWhoMustBeNamed Dec 02 '18 at 07:38
  • @MrReality - The member is physically inside the class object. But it's a regular old int. You can create a reference to it just fine. The only gotcha is that the int must be alive for the reference to be valid. Now, you can refer to `x` inside the class, so you can bind a reference to it, and you can return that reference to the caller. Now the caller has a reference to an int, that is alive. – StoryTeller - Unslander Monica Dec 02 '18 at 07:41
1

"Scope" and "access control" are things that apply to names, not variables. Saying that x is private in Foo means that if we write x in some unrelated context, the name will not be found.

But the variable can still be accessed in other contexts if you don't require looking up the name.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • But shouldn't the lifetime of the variable x end just as get_pvar() ends? This from what I have read is how it works for normal functions, which is why we can't return a reference to a local variable of a function. So, why doesn't this apply to member functions? – HeWhoMustBeNamed Dec 02 '18 at 07:20
  • 1
    @MrReality `obj.x` is a member of `obj` ; it lasts as long as `obj` lasts. `obj.x` is not defined in a function. – M.M Dec 02 '18 at 07:25
  • Ok, thanks. What about the second part of my question: how come we can create a reference to a member variable outside the class in main(), which doesn't even require an object and the dot operator to be used? – HeWhoMustBeNamed Dec 02 '18 at 07:33
  • If `x` is a member of 'obj' it stays alive for as long as 'obj' stays alive. In any case C++ doesn't enforce use of variables or members (or any other memory location) after they were destroyed or de-scoped. – selalerer Dec 02 '18 at 07:39
  • @MrReality in `obj.get_pvar()` the object and the dot operator are used. Also you can think of `return x;` as `return (*this).x;` if you like – M.M Dec 02 '18 at 07:52
  • @M.M., I am referring to variable ref, which is an alias for a class member variable but doesn't require an object and the dot operator to be used. I don't understand why. – HeWhoMustBeNamed Dec 02 '18 at 08:02
  • Scope does affect the lifetime of variables - just not in this particular example. – Peter Dec 02 '18 at 10:29
  • @Peter they are separate concepts; often a variable's lifetime is the same as the scope of the name in its definition , but not always – M.M Dec 02 '18 at 10:33
  • @MrReality `(*this).x` is the expression that ultimately initializes `ref` , it has the object `*this` and the dot operator. You can omit those inside a member function, name lookup inside member functions finds member variables. Once a variable has been initialized you do not need to keep harking back to how it was initialized. Initializing a reference means that the reference's name thereafter names the object in the intitializer. – M.M Dec 02 '18 at 10:35
0

That is how references or pointers work. If you decided to export the address of your private field it can be changed from the outside.

It is a bad practice but the language doesn't have any mechanism to prevent the developer from doing it.

selalerer
  • 3,766
  • 2
  • 23
  • 33
  • But shouldn't the lifetime of a member variable be over just as the member function ends, like it is so for normal functions because of which we can;t return a reference to a local variable defined in the function? – HeWhoMustBeNamed Dec 02 '18 at 07:22