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.

Infiltrator
  • 1,611
  • 1
  • 16
  • 25
  • 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. – Tim Martin Jan 28 '11 at 14:56
  • 1
    @Tim I thought you were having a conversation with yourself for a minute there, until I compared profiles – Abe Voelker Jan 28 '11 at 15:09
  • Yeah, I ought to do something about that. It's not as unique of a name as I thought. :-) – Tim Martin Jan 28 '11 at 15:13
  • 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. – Infiltrator Jan 28 '11 at 15:51
  • 1
    @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. – sbi Jan 28 '11 at 16:46

4 Answers4

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.

sbi
  • 219,715
  • 46
  • 258
  • 445
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 2
    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. – Infiltrator Jan 28 '11 at 16:02
  • @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. http://stackoverflow.com/questions/3364722/accessing-protected-member-of-template-parameter/3365221#3365221 – CB Bailey Jan 28 '11 at 16:06
  • How exactly would I go about fixing the class hierarchy in this (simplified) case? – Infiltrator Jan 28 '11 at 16:15
  • @Tim: The obvious solution would be to make `hp` public as this is the access that you need. – CB Bailey Jan 28 '11 at 16:17
  • 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) – Infiltrator Jan 28 '11 at 16:19
  • @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. – CB Bailey Jan 28 '11 at 16:23
  • @sbi: Thanks, yes, it was a typo and your choice of variable name was what I originally intended. – CB Bailey Jan 28 '11 at 16:25
  • 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? – Infiltrator Jan 28 '11 at 16:26
  • 2
    @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. – sbi Jan 28 '11 at 16:27
  • @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. – CB Bailey Jan 28 '11 at 16:29
  • @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. – Infiltrator Jan 28 '11 at 16:39
  • @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. – sbi Jan 28 '11 at 16:44
  • @sbi: Yeah, I think I'm just going to be forced to go with get() and set() methods. – Infiltrator Jan 28 '11 at 16:48
  • @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](http://stackoverflow.com/questions/4683937/the-use-of-getters-and-setters-for-different-programming-languages/4684309#4684309). – sbi Jan 28 '11 at 16:52
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.

Lee Louviere
  • 5,162
  • 30
  • 54
  • You can't make a class a friend of a variable. Did you mean to say "class" instead? – sbi Jan 28 '11 at 16:30
  • 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. – sbi Jan 28 '11 at 16:31
  • 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. – Infiltrator Jan 28 '11 at 16:35
  • 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. – Lee Louviere Jan 28 '11 at 16:37
  • 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. – CB Bailey Jan 28 '11 at 16:43
  • 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. – Infiltrator Jan 28 '11 at 16:43
  • @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). – jkyle Apr 29 '11 at 02:45
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;
}
thorsten müller
  • 5,621
  • 1
  • 22
  • 30
  • 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. – Infiltrator Jan 28 '11 at 15:57
  • 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. – Infiltrator Jan 28 '11 at 16:18
  • @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. – sbi Jan 28 '11 at 16:33
-4

Try to change to this

 Class Child : public Parent
Damir
  • 54,277
  • 94
  • 246
  • 365
  • 1
    The same problem happens with public inheritance. – Tim Martin Jan 28 '11 at 14:59
  • 1
    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. – CB Bailey Jan 28 '11 at 15:50