Accessing parent's protected variables
Asked Answered
S

4

24

I couldn't think of a better wording for the title, so it is a little misleading, however, I am not talking about a child accessing its variables inherited from its parent, which is easy enough.

What I am talking about is this:

class Parent {
  protected:
     Parent *target;
     int hp;
}

class Child : public Parent {
  public:
     void my_func();
}

void Child::my_func() {
    target->hp -= 50;
}

However, if I try to compile this, it will complain about 'hp' being "private in this context". The problem is that the child is not attempting to access its own parent's variables, but some other class', which may or may not be a Child itself.

An object can access all the variables and methods (public, protected, or private) of another object (two separate instances in memory) that is of the same class, so I thought that it would work with this as well, as it inherits from the class whose variables it's attempting to access, but it seems I was incorrect in assuming so.

Any tips?

P.S. Not to be rude or anything, but I know that I can just create get() and set() methods, but I was hoping for a cleaner way.

Sherl answered 28/1, 2011 at 14:50 Comment(5)
There are numerous errors in your code as written here (incorrectly capitalised class keyword, incorrect inheritance syntax, etc.) which I'm sure are typos that aren't in the original code. It might be useful to get a minimal example that fails to compile, then copy and paste the exact code over here.Blackfellow
@Tim I thought you were having a conversation with yourself for a minute there, until I compared profilesBedstraw
Yeah, I ought to do something about that. It's not as unique of a name as I thought. :-)Blackfellow
Urgh yeah, I wasn't really paying too much attention to that, and it's not letting me edit it now... EDIT: There we go.Sherl
@Abe: I had the same problem. Fortunately, one of the two is the OP, so my FF highlights his name. It's a mess, though.Terminator
R
27

Member functions of a particular class only have access to protected members of base classes that actually are base class subobjects of objects of their own class type (or more derived types).

Members of one class do not have access to protected members of other instances of that base class and so are also forbidden from accessing protected members through a reference or pointer to the base class type even if at runtime that pointer or reference might be to an object that is of the type of the class whose member function is attempting the access. Access control is enforced at compile time.

E.g.

class X
{
protected:
    int z;
};

class Y : X
{
public:
    int f( const Y& y )
    {
        return y.z; // OK
    }

    int g( const X& x )
    {
        return x.z; // Error, Y::g has no access to X::z
    }
};

In your example, in the expression target->hp, the access to target is legal because you are accessing a member of the current object (which has the type of the class of which the function is a member, Child), but the access to the member hp is not legal because the type of target is not a pointer to Child, but a pointer to Parent.

Rycca answered 28/1, 2011 at 15:58 Comment(14)
Perhaps I did not make it clear enough in the OP, but I understand that. I want to know if there is any way I can do this without get() and set() methods.Sherl
@Tim: My answer was trying to help explain your incorrect assumption. There are abuses but you should fix the class hierarchy so that you have the access that you need. #3365222Rycca
How exactly would I go about fixing the class hierarchy in this (simplified) case?Sherl
@Tim: The obvious solution would be to make hp public as this is the access that you need.Rycca
Hm. Perhaps that is oversimplified. I still need "hp" to be inaccessible to everything except what inherits from Parent. P.S. How do you make your words in boxes? (That sounded really stupid)Sherl
@Tim: If I understand what you mean, the nearest access control that does what you want is public. protected is too restrictive for what you need.Rycca
@sbi: Thanks, yes, it was a typo and your choice of variable name was what I originally intended.Rycca
Well, public is TOO loose an access. What I need is for all objects of type Parent, or any type that inherits from Parent, such as Child, to have access to "hp", but at the same time for no other objects of any other types to have access to it. Is that possible?Sherl
@Tim: Public data is frowned upon for good reasons, and protected data is just data that's public to derived classes. I have needed that occasionally over the >15 years I program in C++, but rarely ever in the last decade. A class's state (data) should be manipulated through its member functions, not fiddled with directly. If I run into the need for that, I ask myself what abstraction the base class is supposed to represent, and why a derived class needs to break through this abstraction and access the base class' data directly.Terminator
@Tim: There is nothing between protected and public. Access control in C++ is fairly coarse and is there principally to guide, not enforce. Short of declaring every class derived from Parent a friend of Parent there is no access level that matches exactly what you are trying to do. public is as close as you can get.Rycca
@sbi: I know what you mean, but for some reason (to me) it made sense for instances of the same type to have access to each others variables. Note that in my actual code, I only required read access, not write access, as all the writes were much more complicated and were placed in methods.Sherl
@Tim: That's hackish, but not knowing your code it might well be the best solution to whatever problems you have. Therefore it's good for you that C++ lets you circumvent the problem. However, it's equally good for whoever will have to maintain your code 10 years down the road that you need to make this hack explicit by using friend or public data.Terminator
@sbi: Yeah, I think I'm just going to be forced to go with get() and set() methods.Sherl
@Tim: What would they buy you? If you make them public, everyone can use them to directly access your private variable. You could just as well make the variable public instead. If you make them protected (or private), they have the same problem a protected (or private) variable has. No, getters and setters either solve a problem that doesn't exist or fail to solve an existing problem.Terminator
F
5

This is so easy (meaning the apparent misunderstanding of the OP, is because people aren't taking the time to read the OP).

You simply make the child a friend of the parent's variable that you need to access.

Or, you can make the child a friend of the parent class.

That way any child has access to any parent's member variables, exactly the way you are expecting.

class Child;

class Parent {
  protected:
     Parent *target;
     int hp;
     friend void Child::my_func();
}

class Child : public Parent {
  public:
     void my_func();
}

void Child::my_func() {
    target->hp -= 50;
}

The downside to this is that EVERY child can have access to the variables of EVERY parent. However, you must consider that in your case, the compiler cannot know that Parent *target is the same instance as the child. Given that you named it target, I would expect that having EVERY child have access to variables of EVERY parent is what you want.

Here's another possibility. Have everyone else use an interface to access the parent, and have only your child use the actual parent class. The result is the same though. Every child has access to every parents variables.

You're confusing class with instance. The child has access to the same member variables of the base class that is of the same INSTANCE.

Fahey answered 28/1, 2011 at 16:24 Comment(7)
You can't make a class a friend of a variable. Did you mean to say "class" instead?Terminator
Also note my comments to Tim at Charles' answer. What I said about protected is all the more true about friend: It breaks through a class' abstraction, tightly coupling the friend to the class' implementation. Tight coupling is always bad. Use friend as much as necessary, but as little as possible.Terminator
Copy-paste: Yes, I could do that, and that is my hack for the moment, but it just seems like it breaks the "downwardness" of inheritance. You shouldn't need to friend every class that inherits from it in order to get it to work.Sherl
There, done. It was hard to find an example that exactly matched what you wanted. This is how you make A SINGLE METHOD from one class a friend in another class.Fahey
You can't declare a member of a class a friend unless that class already has a visible definition including a declaration of the member in question. This impossible if the class you're trying to grant access to is going to be derived from the class that is trying to grant the access.Rycca
I know you tried, but again, I don't like making a child a friend of a parent because then I'd have to do so for every single class that inherits from the parent. I think I'm slowly starting to accept that I just can't do what I want.Sherl
@Xaade The example above won't compile (invalid use of incomplete type struct 'Child'). Tested in g++ and clang. You can declare the entire class a friend (friend class Child).Deidradeidre
B
0

hmm, strange nobody mentioned this so far, but you could declare Child to be a friend of Parent (maybe because your code isn't very clear about what exactly you want to do here)

class Parent {
  friend class Child;
  protected:
     int hp;
}

class Child {
  public:
     void my_func();
     Parent *target;
}

this would allow access. alternatively you could write an accessor method that's public:

class Parent {
public:
  get_hp(){return hp;}
protected:
  int hp;
}
Blastoff answered 28/1, 2011 at 15:40 Comment(3)
Yes, I could do that, and that is my hack for the moment, but it just seems like it breaks the "downwardness" of inheritance. You shouldn't need to friend every class that inherits from it in order to get it to work.Sherl
Also, as I said, I'm aware that I can just use set() and get() methods, which is what your example is, essentially. I would like to see if there is any way of doing it similar to the way I'm attempting and you would be able to if you weren't using inheritance.Sherl
@Tim: As I said in my comment to Charles' answer, the problem is that you want to break through the abstraction that's represented by the base class. You need to ask yourself why you want to do that.Terminator
K
-4

Try to change to this

 Class Child : public Parent
Keishakeisling answered 28/1, 2011 at 14:53 Comment(2)
The same problem happens with public inheritance.Blackfellow
It's nothing to do with whether Child inherits publically or privately from Parent it's about whether a Child method is trying to access a protected member of a Parent that is a base class subobject of Child or one that is not.Rycca

© 2022 - 2024 — McMap. All rights reserved.