Hacklang - Why are protected members invariant? - casting

The public member case
With the carte blanche access the calling scope has to them, it's no surprise that public members are invariant:
<?hh // strict
class Foo<+T> {
public function __construct(
public T $v
) {}
}
class ViolateType {
public static function violate(Foo<int> $foo): void {
self::cast_and_set($foo);
echo $foo->v + 1; // string + integer
}
public static function cast_and_set(Foo<arraykey> $foo): void {
$foo->v = "Poof! The integer `violate()` expects is now a string.";
}
}
// call ViolateType::foo(new Foo(1)); and watch the fireworks
The problem here is that both violate and cast_and_set can read and modify the same value (Foo->v) with different expectations of its type.
This problem, however, doesn't seem to exist for protected members.
Attempt to create a violation for protected members
Since the only distinction between private and protected is visibility to descendants, let's take a class (ImplCov) that, outside of some number of protected members, is otherwise validly covariant on a type, and extend it into a class (ImplInv) invariant on that type. Notably, being invariant on T allows me to expose a public setter — violate(T $v): T — where I'll try to break types.
<?hh // strict
// helper class hierarchy
class Base {}
class Derived extends Base {}
class ImplCov<+T> {
public function __construct(
protected T $v
) {}
}
class ImplInv<T> extends ImplCov<T> {
public function violate(T $v): T {
// Try to break types here
}
}
With an instance of ImplInv<Derived>, I'm compelled to cast to an ImplCov<Derived>, then leverage covariance to cast to an ImplCov<Base>. It like the most dangerous thing to do, with all three types referring to the same object. Let's inspect the relationships between each type:
ImplInv<Derived> and ImplCov<Base>: The violation in the public member case occured when the property was changed to a supertype (int->arraykey) or disjoint type with a common supertype (int->string). However, because ImplCov<Base> is covariant on T, there cannot exist methods that can be passed a Base instance and make v a true Base. ImplCov's methods cannot spawn a new Base() either and assign it to v because it doesn't know the eventual type of T.1
Meanwhile, because casting ImplCov<Derived> --> ImplCov<Base> --> ... can only cause it to be less derived, ImplInv::violate(T) is guaranteed to at worst set v to a subtype of the eventual ImplCov's T, guaranteeing a valid cast to that eventual T. The Derived of ImplInv<Derived> can't be cast, so once parameterized, that type is set in stone.
ImplInv<Derived> and ImplCov<Derived>: these can coexist by merit of T being the same between them, the cast being only of the outermost type.
ImplCov<Derived> and ImplCov<Base>: these can coexist by the assumption that ImplCov is validly covariant. The protected visibility of v is indistinguishable from private, since they are the same class.
All of this seems to point to protected visibility being kosher for covariant types. Am I missing something?
1. We can actually spawn new Base() by introducing the super constraint: ImplCov<T super Base>, but this is even weaker, since by definition ImplInv has to parameterize ImplCov in the extends statement with a supertype, making ImplInv's operations with v safe. Plus, ImplCov can't assume anything about the members of T.

Remember that a subclass can modify a protected member of any object of that class, not just $this. So this slight modification of your example above uses a protected member to similarly break the type system -- all we have to do is make ViolateType a subclass of Foo (and it doesn't matter what we set T to, or if we make ViolateType generic or whatever).
<?hh // strict
class Foo<+T> {
public function __construct(
/* HH_IGNORE_ERROR[4120] */
protected T $v
) {}
}
class ViolateType extends Foo<void> {
public static function violate(Foo<int> $foo): void {
self::cast_and_set($foo);
echo $foo->v + 1; // string + integer
}
public static function cast_and_set(Foo<arraykey> $foo): void {
$foo->v = "Poof! The integer `violate()` expects is now a string.";
}
}
This passes the typechecker with only the one error suppression for the protected member -- so allowing protected members to be covariant would break the type system.

Related

C++ protected: fail to access base's protected member from within derived class

Admittedly, this question title sounds pretty much exactly the same as the question you neighbour Mike has repeatedly asked. I found quite a few questions worded the same way, but none was what my question is about.
First of all, I'd like to clarify a few points for the context of this question:
1, c++ access control works on a class basis rather than instance basis. Therefore, the following code is completely valid.
class Base
{
protected:
int b_;
public:
bool IsEqual(const Base& another) const
{
return another.b_ == b_; // access another instance's protected member
}
};
2, I completely understand why the following code is NOT valid - another can be a sibling instance.
class Derived : public Base
{
public:
// to correct the problem, change the Base& to Derived&
bool IsEqual_Another(const Base& another) const
{
return another.b_ == b_;
}
};
Now time to unload my real question:
Assume in the Derived class, I have an array of Base instances. So effectively, Derived IS A Base(IS-A relation), and Derived consists of Base(Composite relation). I read from somewhere that this(refers to the design of both IS-A and Has-A) is a design smell and I should never have a scenario like this in the first place. Well, the mathematical concept of Fractals, for example, can be modelled by both IS-A and Has-A relations. However, let's disregard the opinion on design for a moment and just focus on the technical problem.
class Derived : public Base
{
protected:
Base base_;
public:
bool IsEqual_Another(const Derived& another) const
{
return another.b_ == b_;
}
void TestFunc()
{
int b = base_.b_; // fail here
}
};
The error message has already stated the error very clearly, so there's no need to repeat that in your answer:
Main.cpp:140:7: error: ‘int Base::b_’ is protected
int b_;
^
Main.cpp:162:22: error: within this context
int b = base_.b_;
Really, according to the following 2 facts, the code above should work:
1, C++ access control works on class basis rather than instance basis(therefore, please don't say that I can only access Derived's b_; I can't access a stand alone Base instance's protected members - it's on class basis).
2, Error message says "within this context" - the context is Derived(I was trying to access a Base instance's protected member from within Derived. It's the very feature of a protected member - it should be able to be accessed from within Base or anything that derives from Base.
So why is the compiler giving me this error?
The access rules could in principle have provided an exemption for this special case, where it's known that Base is the most derived class, the dynamic type of the object. But that would have complicated things. C++ is sufficiently complicated.
A simple workaround is to provide a static protected accessor function up in Base.
A more hack'ish workaround is to use the infamous type system loophole for member pointers. But I'd go for the static function, if I had to stick with the basic design. Because I think like there's not much point in saving a few keystrokes when the resulting code is both hard to get right in the first place, and hard to understand for maintainers.
Concrete example:
class Base
{
protected:
int b_;
static
auto b_of( Base& o )
-> int&
{ return o.b; }
public:
auto IsEqual( const Base& another ) const
-> bool
{
return another.b_ == b_; // access another instance's protected member
}
};
2, Error message says "within this context" - the context is Derived(I was trying to access a Base instance's protected member from within Derived. It's the very feature of a protected member- it should be able to be accessed from within Base or anything that derives from Base.
Okay, had to go to the standard for this one.
So you're asking, "Why isn't it possible?" The answer: Because of how the standard really defines protected member access:
§ 11.4
Protected member access
[1]
An additional access check beyond those described earlier in Clause 11 is applied when a non-static data
member or non-static member function is a protected member of its naming class...As described
earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C.
(emphasis mine)
So let's go over your examples to see what's what.
class Base
{
protected:
int b_;
public:
bool IsEqual(const Base& another) const
{
return another.b_ == b_; // access another instance's protected member
}
};
No problem. another.b_ is Base::b_, and we're accessing it from a member function Base::IsEqual(const Base&) const.
class Derived : public Base
{
public:
// to correct the problem, change the Base& to Derived&
bool IsEqual_Another(const Base& another) const
{
return another.b_ == b_;
}
};
Here, we're accessing Base::b_ again, but our context is a member function Derived::IsEqual_Another(const Base&) const, which isn't a member of Base. So no go.
Now for the alleged culprit.
class Derived : public Base
{
protected:
Base bases_[5];
public:
bool IsEqual_Another(const Derived& another) const
{
return another.b_ == b_;
}
void TestFunc()
{
int b = bases_[0].b_; // fail here
}
};
bases_[0].b_ is accessing the protected Base::b_, inside the context of Derived::TestFunc(), which isn't a member (or friend...) of Base.
So looks like the compiler is acting in accordance with the rules.
I am just turning my comments into an answer because I find the issue interesting. In particular that in the following minimal example D doesn't compile baffled me:
class B { protected: int i; };
class D : public B { int f(B &b){ return b.i; } };
After all, a D is a B and should be able to do all that a B can do (except access B's private members), shouldn't it?
Apparently, the language designers of both C++ and C# found that too lenient. Eric Lippert commented one of his own blog posts saying
But that’s not the kind of protection we’ve chosen as interesting or valuable. "Sibling" classes do not get to be friendly with each other because otherwise protection is very little protection.
EDIT:
Because there seems to be some confusion about the actual rule laid forth in 11.4 I'll parse it and illustrate the basic idea with a short example.
The purpose of the section is laid out, and what it applies to (non-static members).
An additional access check beyond those described earlier in Clause 11
is applied when a non-static data member or non-static member function
is a protected member of its naming class (11.2)
The naming class in the example below is B.
Context is established by summarising the chapter so far (it defined access rules for protected members). Additionally a name for a "class C" is introduced: Our code is supposed to reside inside a member or friend function of C, i.e. has C's access rights.
As described earlier, access to a protected member is
granted because
the reference occurs in a friend or member of some
class C.
"Class C" is also class C in the example below.
Only now the actual check is defined. The first part deals with pointers to members, which we ignore here. The second part concerns your everyday accessing a member of an object, which logically "involve a (possibly implicit) object expression".
It's just the last sentence which describes the "additional check" this whole section was for:
In this case, the class of the object expression
[through which the member is accessed -pas]
shall be C or a class derived from C.
The "object expression" can be things like a variable,
a return value of a function, or a dereferenced pointer.
The "class of the object expression" is a compile time
property, not a run time property; access through one
and the same object may be denied or granted depending
on the type of the expression used to access the member.
This code snippet demonstrates that.
class B { protected: int b; };
class C: public B
{
void f()
{
// Ok. The expression of *this is C (C has an
// inherited member b which is accessible
// because it is not declared private in its
// naming class B).
this->b = 1;
B *pb = this;
// Not ok -- the compile time
// type of the expression *pb is B.
// It is not "C or a class derived from C"
// as mandated by 11.4 in the 2011 standard.
pb->b = 1;
}
};
I initially wondered about this rule and assume the following rationale:
The issue at hand is data ownership and authority.
Without code inside B explicitly providing access (by making C a friend or by something like Alf's static accessor) no other classes except those who "own" the data are allowed to access it. This prevents gaining illicit access to the protected members of a class by simply defining a sibling and modifying objects of the original derived class through the new and before unknown sibling. Stroustrup speaks of "subtle errors" in this context in the TCPPL.
While it would be safe to access (different) objects of the original base class from a derived class' code, the rule is simply concerned with expressions (a compile time property) and not objects (a run time property). While static code analysis may show that an expression of some type Base actually never refers to a sibling, this is not even attempted, similar to the rules concerning aliasing. (Maybe that is what Alf meant in his post.)
I imagine the underlying design principle is the following: Guaranteeing ownership and authority over data gives a class the guarantee that it can maintain invariants related to the data ("after changing protected a always also change b"). Providing the possibility to change a protected property from by a sibling may break the invariant -- a sibling does not know the details of its sibling's implementation choices (which may have been written in a galaxy far, far away). A simple example would be a Tetragon base class with protected width and height data members plus trivial public virtual accessors. Two siblings derive from it, Parallelogram and Square. Square's accessors are overridden to always also set the other dimension in order to preserve a square's invariant of equally long sides, or they only just use one of the two. Now if a Parallelogram could set a Square's width or height directly through a Tertragon reference they would break that invariant.
This has nothing to do with bases_ being protected in Derived, it is all about b_ being protected in Base.
As you have already stated, Derived can only access protected members of its base class, not of any other Baseobjects. Not even if they are members of Derived.
If you really need access, you can make Derived a friend on Base.
Ok, I've been bothered by this wicked thing for a night. Endless discussions and the ambiguity of clause 11.4(as quoted by Yam marcovic)
§ 11.4 Protected member access
[1] An additional access check beyond those described earlier in Clause 11 is applied when a non-static data member or non-static member function is a protected member of its naming class...As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C.
have burned me out. I decided to resort to the gcc source code(gcc 4.9.2 in my case) to check how those gcc guys understood the clause 11.4, and what check exactly the C++ standards wants to do and how those checks are supposed to be done.
In gcc/cp/search.c:
/* Returns nonzero if it is OK to access DECL through an object
indicated by BINFO in the context of DERIVED. */
static int protected_accessible_p (tree decl, tree derived, tree binfo)
{
access_kind access;
/* We're checking this clause from [class.access.base]
m as a member of N is protected, and the reference occurs in a
member or friend of class N, or in a member or friend of a
class P derived from N, where m as a member of P is public, private
or protected.
Here DERIVED is a possible P, DECL is m and BINFO_TYPE (binfo) is N. */
/* If DERIVED isn't derived from N, then it can't be a P. */
if (!DERIVED_FROM_P (BINFO_TYPE (binfo), derived))
return 0;
access = access_in_type (derived, decl);
/* If m is inaccessible in DERIVED, then it's not a P. */
if (access == ak_none)
return 0;
/* [class.protected]
When a friend or a member function of a derived class references
a protected nonstatic member of a base class, an access check
applies in addition to those described earlier in clause
_class.access_) Except when forming a pointer to member
(_expr.unary.op_), the access must be through a pointer to,
reference to, or object of the derived class itself (or any class
derived from that class) (_expr.ref_). If the access is to form
a pointer to member, the nested-name-specifier shall name the
derived class (or any class derived from that class). */
if (DECL_NONSTATIC_MEMBER_P (decl))
{
/* We can tell through what the reference is occurring by
chasing BINFO up to the root. */
tree t = binfo;
while (BINFO_INHERITANCE_CHAIN (t))
t = BINFO_INHERITANCE_CHAIN (t);
if (!DERIVED_FROM_P (derived, BINFO_TYPE (t)))
return 0;
}
return 1;
}
The most interesting part is this:
if (DECL_NONSTATIC_MEMBER_P (decl))
{
/* We can tell through what the reference is occurring by
chasing BINFO up to the root. */
tree t = binfo;
while (BINFO_INHERITANCE_CHAIN (t))
t = BINFO_INHERITANCE_CHAIN (t);
if (!DERIVED_FROM_P (derived, BINFO_TYPE (t)))
return 0;
}
1) derived in the code is the context, which in my case is the Derived class;
2) binfo in the code represents the instance whose non-static protected member is access, which in my case is base_, Derived's protected data member Base instance;
3) decl in the code represents base_.b_.
What gcc did when translating my code in question was:
1) check if base_.b_ is non-static protected member? yes of course, so enter the if;
2) climb up the inheritance tree of base_;
3) figure out what actual type base_ is; of course, it's Base
4) check if the result in 3) which is Base, derives from Derived. Of course that's a negative. Then return 0 - access denied.
Apparently, according to gcc's implementation, the "additional check" requested by the C++ standard is the type check of the instance through which the protected member gets accessed. Although the C++ standard did not explicitly mention what check should be done, I think gcc's check is the most sensible and plausible one - it's probably the kind of check the C++ standard wants. And then the question really boils down to the rationale for the standard to request an additional check like this. It effectively makes the standard contradict itself. Getting rid of that interesting section(It seems to me that the C++ standard is asking for inconsistency deliberately), the code should work perfectly. In particular, the sibling problem won't occur as it will be filtered by the statement:
if (!DERIVED_FROM_P(BINFO_TYPE(t), derived))
return 0;
Regarding the kind of protection(protected does not work purely on class, but on BOTH class AND instance) mentioned by Peter and the post(by Eric Lippert) he shared, I personally totally agree with that. Unfortunately, by looking at the C++ standard's wording, it doesn't; if we accept that the gcc implementation is an accurate interpretation of the standard, then what the C++ standard really asks for is, a protected member can be accessed by its naming class or anything that derives from the naming class; however, when the protected member is accessed via an object, make sure the owner object's type is the same as the calling context's type. Looks like the standard just wants to make an exception for the clarification point 1 in my original question.
Last but not least, I'd like to thank Yam marcovic for pointing out clause 11.4. You are the man, although your explanation wasn't quite right - the context does not have to be Base, it can be Base or anything derived from Base. The catch was in the type check of the instance through which the non-static protected member was accessed.
There are a couple of long answers, and quotes from the standard that are correct. I intend on providing a different way of looking at what protected really means that might help understanding.
When a type inherits from a different type, it gets a base sub-object. The protected keyword means that any derived type can access this particular member within the sub-object that it contains due to the inheritance relationship. The keyword grants access to specific object(s), not to any object of type base.

If access modifiers are class-level, why does protected seem not to? [duplicate]

Why does this compile:
class FooBase
{
protected:
void fooBase(void);
};
class Foo : public FooBase
{
public:
void foo(Foo& fooBar)
{
fooBar.fooBase();
}
};
but this does not?
class FooBase
{
protected:
void fooBase(void);
};
class Foo : public FooBase
{
public:
void foo(FooBase& fooBar)
{
fooBar.fooBase();
}
};
On the one hand C++ grants access to private/protected members for all instances of that class, but on the other hand it does not grant access to protected members of a base class for all instances of a subclass.
This looks rather inconsistent to me.
I have tested compiling with VC++ and with ideone.com and both compile the first but not the second code snippet.
When foo receives a FooBase reference, the compiler doesn't know whether the argument is a descendant of Foo, so it has to assume it's not. Foo has access to inherited protected members of other Foo objects, not all other sibling classes.
Consider this code:
class FooSibling: public FooBase { };
FooSibling sib;
Foo f;
f.foo(sib); // calls sib.fooBase()!?
If Foo::foo can call protected members of arbitrary FooBase descendants, then it can call the protected method of FooSibling, which has no direct relationship to Foo. That's not how protected access is supposed to work.
If Foo needs access to protected members of all FooBase objects, not just those that are also known to be Foo descendants, then Foo needs to be a friend of FooBase:
class FooBase
{
protected:
void fooBase(void);
friend class Foo;
};
The C++ FAQ summarizes this issue nicely:
[You] are allowed to pick your own pockets, but you are not allowed to pick your father's pockets nor your brother's pockets.
The key point is that protected grants you access to your own copy of the member, not to those members in any other object. This is a common misconception, as more often than not we generalize and state protected grants access to the member to the derived type (without explicitly stating that only to their own bases...)
Now, that is for a reason, and in general you should not access the member in a different branch of the hierarchy, as you might break the invariants on which other objects depend. Consider a type that performs an expensive calculation on some large data member (protected) and two derived types that caches the result following different strategies:
class base {
protected:
LargeData data;
// ...
public:
virtual int result() const; // expensive calculation
virtual void modify(); // modifies data
};
class cache_on_read : base {
private:
mutable bool cached;
mutable int cache_value;
// ...
virtual int result() const {
if (cached) return cache_value;
cache_value = base::result();
cached = true;
}
virtual void modify() {
cached = false;
base::modify();
}
};
class cache_on_write : base {
int result_value;
virtual int result() const {
return result_value;
}
virtual void modify() {
base::modify();
result_value = base::result();
}
};
The cache_on_read type captures modifications to the data and marks the result as invalid, so that the next read of the value recalculates. This is a good approach if the number of writes is relatively high, as we only perform the calculation on demand (i.e. multiple modifies will not trigger recalculations). The cache_on_write precalculates the result upfront, which might be a good strategy if the number of writes is small, and you want deterministic costs for the read (think low latency on reads).
Now, back to the original problem. Both cache strategies maintain a stricter set of invariants than the base. In the first case, the extra invariant is that cached is true only if data has not been modified after the last read. In the second case, the extra invariant is that result_value is the value of the operation at all times.
If a third derived type took a reference to a base and accessed data to write (if protected allowed it to), then it would break with the invariants of the derived types.
That being said, the specification of the language is broken (personal opinion) as it leaves a backdoor to achieve that particular result. In particular, if you create a pointer to member of a member from a base in a derived type, access is checked in derived, but the returned pointer is a pointer to member of base, which can be applied to any base object:
class base {
protected:
int x;
};
struct derived : base {
static void modify( base& b ) {
// b.x = 5; // error!
b.*(&derived::x) = 5; // allowed ?!?!?!
}
}
In both examples Foo inherits a protected method fooBase. However, in your first example you try to access the given protected method from the same class (Foo::foo calls Foo::fooBase), while in the second example you try to access a protected method from another class which isn't declared as friend class (Foo::foo tries to call FooBase::fooBase, which fails, the later is protected).
In the first example you pass an object of type Foo, which obviously inherits the method fooBase() and so is able to call it. In the second example you are trying to call a protected function, simply so, regardless in which context you can't call a protected function from a class instance where its declared so.
In the first example you inherit the protected method fooBase, and so you have the right to call it WITHIN Foo context
I tend to see things in terms of concepts and messages. If your FooBase method was actually called "SendMessage" and Foo was "EnglishSpeakingPerson" and FooBase was SpeakingPerson, your protected declaration is intended to restrict SendMessage to between EnglishSpeakingPersons (and subclasses eg: AmericanEnglishSpeakingPerson, AustralianEnglishSpeakingPerson) . Another type FrenchSpeakingPerson derived from SpeakingPerson would not be able to receive a SendMessage, unless you declared the FrenchSpeakingPerson as a friend, where 'friend' meant that the FrenchSpeakingPerson has a special ability to receive SendMessage from EnglishSpeakingPerson (ie can understand English).
In addition to hobo's answer you may seek a workaround.
If you want the subclasses to want to call the fooBase method you can make it static. static protected methods are accessible by subclasses with all arguments.
You can work around without a friend like so...
class FooBase
{
protected:
void fooBase(void);
static void fooBase(FooBase *pFooBase) { pFooBase->fooBase(); }
};
This avoids having to add derived types to the base class. Which seems a bit circular.

What's the reason for protected member accessibility additional check in C++?

I just run to this issue, and know from the C++ standard define it as following (emphasize mine)
An additional access check beyond those described earlier in Clause 11
is applied when a non-static data member or non-static member function
is a protected member of its naming class (11.2)115 As described
earlier, access to a protected member is granted because the reference
occurs in a friend or member of some class C. If the access is to form
a pointer to member (5.3.1), the nested-name-specifier shall denote C
or a class derived from C. All other accesses involve a (possibly
implicit) object expression (5.2.5). In this case, the class of the
object expression shall be C or a class derived from C.
Code snippet:
class Base
{
protected:
int i;
};
class Derived : public Base
{
public:
// I cannot define it as void memfunc(Derived* obj) because of signature requirement.
void memfunc(Base* obj)
{
obj->i = 0; // ERROR, cannot access private member via Base*
Derived* dobj = (Derived*)(obj);
dobj->i = 0; // OK
}
};
So what's the reason for this check? Why C++ standard bother to limit access protected member via base class pointer?
Not Duplicate to: Accessing protected members in a derived class, I want to ask for the reason to prohibit it in standard.
Just because you derive from a Base doesn't mean you should be allowed to access protected member of any other class deriving from Base. Imagine a class Derived2 provided by a library which inherits from Base. This way you'll be able to get a base object of Derived2 and do whatever you want with it in the code of Derived.
So basically the standard ensures that you only modify protected members that you inherited and not mess with the integrity of unrelated siblings classes.

Do Sub-Classes Really Inherit Private Member Variables?

Basically as far as I know, when you create a base class with a public, protected, and private section and variables/functions in each the public and protected sections will get inherited into the appropriate section of the sub-class (defined by class subclass : private base, which will take all public and protected members of base and put them into public, changing the word private to public puts them all in public and changing it to protected puts them all into protected).
So, when you create a sub-class you never receive anything from the private section of the previous class (the base class in this case), if this is true then an object of the sub-class should never have it's own version of a private variable or function from the base class correct?
Let's run over an example:
#include <iostream>
class myClass // Creates a class titled myClass with a public section and a private section.
{
public:
void setMyVariable();
int getMyVariable();
private:
int myVariable; // This private member variable should never be inherited.
};
class yourClass : public myClass {}; // Creates a sub-class of myClass that inherits all the public/protected members into the
// public section of yourClass. This should only inherit setMyVariable()
// and getMyVariable() since myVariable is private. This class does not over-ride any
// functions so it should be using the myClass version upon each call using a yourClass
// object. Correct?
int main()
{
myClass myObject; // Creates a myClass object called myObject.
yourClass yourObject; // Creates a yourClass object called yourObject
yourObject.setMyVariable(); // Calls setMyVariable() through yourObject. This in turn calls the myClass version of it because
// there is no function definition for a yourClass version of this function. This means that this
// can indeed access myVariable, but only the myClass version of it (there isn't a yourClass
// version because myVariable is never inherited).
std::cout << yourObject.getMyVariable() << std::endl; // Uses the yourClass version of getMyVariable() which in turn
// calls the myClass version, thus it returns the myClass myVariable
// value. yourClass never has a version of myVariable Correct?
std::cout << myObject.getMyVariable() << std::endl; // Calls the myClass version of getMyVariable() and prints myVariable.
return 0;
}
void myClass::setMyVariable()
{
myVariable = 15; // Sets myVariable in myClass to 15.
}
int myClass::getMyVariable()
{
return myVariable; // Returns myVariable from myClass.
}
Now, in theory based on what I think, this should print:
15
15
Due to it simply always using the myClass version of the functions (thus using the myClass myVariable). But, strangely, this is not the case. The result of running this program prints:
15
0
This makes me wonder, are we actually not only inheriting myVariable, but do we also have the ability to mess around with it? Clearly this is creating an alternate version of myVariable somehow otherwise there wouldn't be a 0 for the myClass version. We are indeed editing a second copy of myVariable by doing all this.
Can someone please explain this all to me, this has torn apart my understanding of inheritance.
Basically as far as I know, when you create a base class with a public, protected, and private section and variables/functions in each the public and protected sections will get inherited into the appropriate section of the sub-class (defined by class subclass : private base, which will take all public and private members of base and put them into public, changing the word private to public puts them all in public and changing it to protected puts them all into protected).
There's a bit of confusion in this statement.
Recall that inheritance is defined for classes and structs in C++. Individual objects (ie. instances) do not inherit from other objects. Constructing an object using other objects is called composition.
When a class inherits from another class, it gets everything from that class, but the access level of the inherited fields may inhibit their use within the inheritor.
Furthermore, there are 3 kinds of inheritance for classes: private (which is the default), protected, and public. Each of them changes the access level of a class properties and methods when inherited by a subclass.
If we order the access levels in this manner: public, protected, private, from the least protected to the most protected, then we can define the inheritance modifiers as raising the access levels of the inherited class fields to at least the level they designate, in the derived class (ie. the class inheriting).
For instance, if class B inherits from class A with the protected inheritance modifier:
class B : protected A { /* ... */ };
then all the fields from A will have at least the protected level in B:
public fields become protected (public level is raised to protected),
protected fields stay protected (same access level, so no modification here),
private fields stay private (the access level is already above the modifier)
"When you create a sub-class you never receive anything from the private section of the [base class]. If this is true then an object of the sub-class should never have it's own version of a private variable or function from the base class, correct?"
No. The derived class inherits all the members of the base class, including the private ones. An object of the inherited class has those private members, but does not have direct access to them. It has access to public members of the base class that may have access to those members, but it (the derived class) may not have new member functions with such access:
class yourClass : public myClass
{
public:
void playByTheRules()
{
setMyVariable(); // perfectly legal, since setMyVariable() is public
}
void tamperWithMyVariable()
{
myVariable = 20; // this is illegal and will cause a compile-time error
}
};
myObject and yourObject are two different objects! Why should they share anything?
Think about it that way: Forget about inheritance and suppose you have a class Person with private int age; and public void setAge (int age) {...}. You then instantiate two objects:
Person bob;
Person bill;
bob.setAge(35);
Would you expect Bill to be 35 now, too? You wouldn't, right? Similarly, your myObject doesn't share its data with yourObject.
In response to your comment:
The class yourClass inherits from myClass. That means that both yourObject and myObject have their own myVariable, the latter obviously by definition, the former inherited from myClass.
Physically, every single member( including member functions) of base class goes into the subclass. Doesn't matter if they are private. Doesn't matter if you inherit them publically/protected-ly/privately. So in your example, yourClass contains all three of getMyVariable(), setMyVariable() and myVariable. All this is pretty simple, okay?
What matters is how we can access them. It is like when a file is deleted on your system. So, you should first understand the difference between a member being not there and a member being there but inaccessible. Assume for now that all inheritance takes place publically. Then, all public members of base class are public in derived class, protected members are protected and private members are inaccessible. They are inaccessible and not non-existent because there can be some member functions in protected and public sections in base class which access the private members of base class. Thus, we need all those private members of base which are accessed by public and protected member functions of base, for their functionality. Since there is no way that we can determine which member is needed by which member function in a simple manner, we include all private members of the base class in derived class. All this simply means that in a derived class, a private member can be modified by only through the base class' member functions.
Note: every private member has to be accessed, directly or indirectly [through another private member function which in turn is called by a public/protected member function] by a public/protected meber function, else it has no use.
So, we know till now that a private member variable of base class has its use in derived class i.e. for the functionality of its public/protected member functions. But they can't be accessed directly in base class.
Now, we turn our attention to private/public inheritance. For public inheritance, it means that all the accessible members of base class (that is, the public and protected members) can not be at a level more permissive than public. Since, public is the most permissive level, public and protected members remain public. But at protected and private inheritance, both become protected and private in the derived class, respectively. Inthe latter case, since all these members are private, they can't be accessed further in the hierarchy chain, but can be accessed by the given derived class all the same.
Thus, the level of each base class member in derived class is the lesser of their level in derived class () and the type of inheritance (public/protected/private).
Same concept applies to the functions outside the class. For them private and protected members are inaccessible but they do exist and can be accessed by the public member functions.
And taking your case as a final example, setMyvariable() and getMyVariable() can access myVariable in the derived class. But no function specified in derived class can access myVariable. Modifying your class:
class myClass
{
public:
void setMyVariable();
int getMyVariable();
private:
int myVariable;
};
class yourClass : public myClass
{
public:
// void yourFunction() { myVariable = 1; }
/*Removing comment creates error; derived class functions can't access myVariable*/
};
Further: you can add exceptions to the type of inheritance too e.g. a private inheritance except a member made public in derived class. But that is another question altogether.
You never call myObject.setMyVariable(), so myObject.getMyVariable() will not return 15.
private does not imply static.
After:
class yourClass : public myClass {};
there is still only one member variable. But there are two ways of accessing it by name: myClass::myVariable, and yourClass::myVariable.
In these expressions, the class name is known as the naming class. The second key thing to understand is that access rights apply to the combination of naming class and member name; not just to the member name and not to the variable itself.
If a member is mentioned without explicitly having the naming class present, then the naming class is inferred from the type of the expression on the left of the . or -> that named the member (with this-> being implied if there is no such expression).
Furthermore, there are really four possible types of access: public, protected, private, and no access. You can't declare a member as having no access, but that situation arises when a private member is inherited.
Applying all this theory to your example:
The name myClass::myVariable is private.
The name yourClass::myVariable is no access.
To reiterate, there is only actually one variable, but it may be named in two different ways, and the access rights differ depending on which name is used.
Finally, back to your original example. myObject and yourObject are different objects. I think what you intended to write, or what you are mentally imagining is actually this situation:
yourClass yourObject;
myClass& myObject = yourObject;
// ^^^
which means myObject names the base class part of yourObject. Then after:
yourObject.setMyVariable();
the variable is set to 15, and so
std::cout << myObject.getMyVariable() << std::endl;
would output 15 because there is indeed only one variable.
This may help
#include<iostream>
using namespace std;
class A
{
int b;
};
class B : private A
{
};
int main()
{
C obj;
cout<<sizeof(obj);
return 0;
}

accessing a protected member of a base class in another subclass

Why does this compile:
class FooBase
{
protected:
void fooBase(void);
};
class Foo : public FooBase
{
public:
void foo(Foo& fooBar)
{
fooBar.fooBase();
}
};
but this does not?
class FooBase
{
protected:
void fooBase(void);
};
class Foo : public FooBase
{
public:
void foo(FooBase& fooBar)
{
fooBar.fooBase();
}
};
On the one hand C++ grants access to private/protected members for all instances of that class, but on the other hand it does not grant access to protected members of a base class for all instances of a subclass.
This looks rather inconsistent to me.
I have tested compiling with VC++ and with ideone.com and both compile the first but not the second code snippet.
When foo receives a FooBase reference, the compiler doesn't know whether the argument is a descendant of Foo, so it has to assume it's not. Foo has access to inherited protected members of other Foo objects, not all other sibling classes.
Consider this code:
class FooSibling: public FooBase { };
FooSibling sib;
Foo f;
f.foo(sib); // calls sib.fooBase()!?
If Foo::foo can call protected members of arbitrary FooBase descendants, then it can call the protected method of FooSibling, which has no direct relationship to Foo. That's not how protected access is supposed to work.
If Foo needs access to protected members of all FooBase objects, not just those that are also known to be Foo descendants, then Foo needs to be a friend of FooBase:
class FooBase
{
protected:
void fooBase(void);
friend class Foo;
};
The C++ FAQ summarizes this issue nicely:
[You] are allowed to pick your own pockets, but you are not allowed to pick your father's pockets nor your brother's pockets.
The key point is that protected grants you access to your own copy of the member, not to those members in any other object. This is a common misconception, as more often than not we generalize and state protected grants access to the member to the derived type (without explicitly stating that only to their own bases...)
Now, that is for a reason, and in general you should not access the member in a different branch of the hierarchy, as you might break the invariants on which other objects depend. Consider a type that performs an expensive calculation on some large data member (protected) and two derived types that caches the result following different strategies:
class base {
protected:
LargeData data;
// ...
public:
virtual int result() const; // expensive calculation
virtual void modify(); // modifies data
};
class cache_on_read : base {
private:
mutable bool cached;
mutable int cache_value;
// ...
virtual int result() const {
if (cached) return cache_value;
cache_value = base::result();
cached = true;
}
virtual void modify() {
cached = false;
base::modify();
}
};
class cache_on_write : base {
int result_value;
virtual int result() const {
return result_value;
}
virtual void modify() {
base::modify();
result_value = base::result();
}
};
The cache_on_read type captures modifications to the data and marks the result as invalid, so that the next read of the value recalculates. This is a good approach if the number of writes is relatively high, as we only perform the calculation on demand (i.e. multiple modifies will not trigger recalculations). The cache_on_write precalculates the result upfront, which might be a good strategy if the number of writes is small, and you want deterministic costs for the read (think low latency on reads).
Now, back to the original problem. Both cache strategies maintain a stricter set of invariants than the base. In the first case, the extra invariant is that cached is true only if data has not been modified after the last read. In the second case, the extra invariant is that result_value is the value of the operation at all times.
If a third derived type took a reference to a base and accessed data to write (if protected allowed it to), then it would break with the invariants of the derived types.
That being said, the specification of the language is broken (personal opinion) as it leaves a backdoor to achieve that particular result. In particular, if you create a pointer to member of a member from a base in a derived type, access is checked in derived, but the returned pointer is a pointer to member of base, which can be applied to any base object:
class base {
protected:
int x;
};
struct derived : base {
static void modify( base& b ) {
// b.x = 5; // error!
b.*(&derived::x) = 5; // allowed ?!?!?!
}
}
In both examples Foo inherits a protected method fooBase. However, in your first example you try to access the given protected method from the same class (Foo::foo calls Foo::fooBase), while in the second example you try to access a protected method from another class which isn't declared as friend class (Foo::foo tries to call FooBase::fooBase, which fails, the later is protected).
In the first example you pass an object of type Foo, which obviously inherits the method fooBase() and so is able to call it. In the second example you are trying to call a protected function, simply so, regardless in which context you can't call a protected function from a class instance where its declared so.
In the first example you inherit the protected method fooBase, and so you have the right to call it WITHIN Foo context
I tend to see things in terms of concepts and messages. If your FooBase method was actually called "SendMessage" and Foo was "EnglishSpeakingPerson" and FooBase was SpeakingPerson, your protected declaration is intended to restrict SendMessage to between EnglishSpeakingPersons (and subclasses eg: AmericanEnglishSpeakingPerson, AustralianEnglishSpeakingPerson) . Another type FrenchSpeakingPerson derived from SpeakingPerson would not be able to receive a SendMessage, unless you declared the FrenchSpeakingPerson as a friend, where 'friend' meant that the FrenchSpeakingPerson has a special ability to receive SendMessage from EnglishSpeakingPerson (ie can understand English).
In addition to hobo's answer you may seek a workaround.
If you want the subclasses to want to call the fooBase method you can make it static. static protected methods are accessible by subclasses with all arguments.
You can work around without a friend like so...
class FooBase
{
protected:
void fooBase(void);
static void fooBase(FooBase *pFooBase) { pFooBase->fooBase(); }
};
This avoids having to add derived types to the base class. Which seems a bit circular.