0

since I don't feel super comfortable with protected, private inheritance in C++ I googled it up and came up with this stackoverflow answer: Difference between private, public, and protected inheritance . Nice! I thought, let us try this out. So I wrote a small example program to test this and wrote the exptected output as comment.

 class Person{
   public: virtual void publicInterface() {}
   protected: virtual void protectedInterface() {}
   private: virtual void privateInterface() {}
    };

    class Professor : public Person  {};
    class Teacher : protected Person { 
    public: void teachPublic(){publicInterface();}
    public: void teachProtected(){protectedInterface();}
    public: void teachPrivate(){privateInterface();}        // not compiling
    };
    class Student : private Person {
    public: void learnPublic(){publicInterface();}
    public: void learnProtected(){protectedInterface();} 
    public: void learnPrivate(){privateInterface();}        // not compiling
    };
    int main()
    {
        Person* p = new Person();       // ok is-a
    Person* pro = new Professor();  // ok    is-a
        Person* t = new Teacher();      // not compiling! No is-a relat.
        Person* s = new Student();      // not compiling! No is-a
        Teacher* t2 = new Teacher();        // ok
        Student* s2 = new Student();    // ok
        pro->publicInterface();     // ok
    t2->publicInterface();      // not compiling
    s2->publicInterface();      // not compiling
    t2->teachPublic();          // ok
    t2->teachProtected();           // ok
    t2->teachPrivate();         // not compiling
    s2->learnPublic();          // ok
    s2->learnProtected();       // not compiling   <-- compiles, but why?
    s2->learnPrivate();         // not compiling
    }

Running this it does mostly exactly what I would expect. However, the second last line seem to compile - which is kind of not exptected from the described behaviour of private inheritance.

Has someone an idea why this is compiling?

Simon
  • 706
  • 6
  • 23
  • This is not java, you don't need `public:` on the each line. – HolyBlackCat Jan 14 '18 at 15:34
  • 1
    It compiles because `learnProtected` is a public member function of `Student`, it's irrevelant what it does or doesn't call. – HolyBlackCat Jan 14 '18 at 15:35
  • @HolyBlackCat Is correct. Remember that typically (with non-inliine functions) the compiler cannot access the function implementation at all at the point of call. What it can see is the *declaration* in the class declaration. – Peter - Reinstate Monica Jan 14 '18 at 15:36
  • @HolyBlackCat, right that is correct. However, than the call inside learn protected should not compile `public: void learnProtected(){protectedInterface();}` – Simon Jan 14 '18 at 15:39
  • Type of inheritance doesn't matter inside the derived class. It only matters outside. – HolyBlackCat Jan 14 '18 at 15:42
  • @HolyBlackCat, you mean ".. and can call .. " , right? Hmm, but still something does not make sense for me yet. Student inherits only privately from Person so it shouldn't be allowed to call a protected function, right? – Simon Jan 14 '18 at 15:48
  • .. or phrased differently, what is than the difference between private and protected inheritance? – Simon Jan 14 '18 at 15:49
  • 1
    Private inheritance: The class itself knows what it inherits from and can use all non-private functions from the base, but outside code doesn't know that the class inherits from the base. Protected inheritanace: Same, but classes derived from this class know that it's derived from the base. – HolyBlackCat Jan 14 '18 at 16:06
  • @HolyBlackCat "_outside code doesn't know that the class inherits from the base_" Wrong! Outside code (meaning any code that isn't member or friend) just cannot use a derived to base **conversion**. – curiousguy Jun 23 '18 at 01:38
  • @curiousguy Huh. Maybe I'm missing something, but can you provide an example demonstrating that it's wrong? – HolyBlackCat Jun 23 '18 at 09:31
  • @HolyBlackCat You cannot refer in valid code to base class data members member to modify them or to function to call them; but name lookup still sees these members and thus they **hide** global declarations with the same name. They are *not* invisible. – curiousguy Jun 23 '18 at 22:02
  • The use of private inheritance can also be observed by well formed code that doesn't have access. [See f.ex.](https://onlinegdb.com/r1cnBB2bX) produces "(is_base_of::value) 1 (is_base_of::value) 1" which means that private inheritance is a property measurable by type traits. – curiousguy Jun 23 '18 at 22:15
  • @curiousguy That's some good points, thanks. – HolyBlackCat Jun 23 '18 at 22:18
  • See also [that multiple inheritance](https://onlinegdb.com/B1EIDS2-m) (note that this is *non* virtual inheritance): in `Most_der` there are two base class subobjects `A` but only one is accessible; direct derived to base conversion from `Most_der` to `A` is non-the-less ambiguous, because access checking isn't done before the conversion resolution: a conversion must be to only one base subobject (OTOH, in case of virtual inheritance, there is one subobject of a type, with many paths). – curiousguy Jun 23 '18 at 22:23
  • @HolyBlackCat There is also the fact that a virtual function in a base class can be overridden without referring to its declaration, so without access control. So private virtual functions can be overridden, or virtual functions in private base classes. – curiousguy Jun 24 '18 at 02:07

3 Answers3

0

The method learnProtected is public. Therefore it is callable "from outside" (i.e. via s2).

The method protectedInterface is protected, which means it is only accessible by the members and friends of Person or by the members of any class derived from Person. Student class is derived from Person and therefore learnProtected can call protectedInterface.

The fact that it privately inherits from Person does only affect the external visibility of the Base class and members. Student itself can access any public or protected member of Person no matter which inheritance (private, protected, public) is used.

Pixelchemist
  • 24,090
  • 7
  • 47
  • 71
0

yes, thank you HolyBlackCat!

I have now an example that explains for me the behaviour of private inheritance:

    class Person{
    public: virtual void publicInterface() {}
    protected: virtual void protectedInterface() {}
    private: virtual void privateInterface() {}
};
class Professor : public Person  {};
class Teacher : protected Person { 
public: void teachPublic(){publicInterface();}
void teachProtected(){protectedInterface();}    // ok!
    void teachPrivate(){privateInterface();}        // not compiling
};
class Student : private Person {
public: void learnPublic(){publicInterface();}
void learnProtected(){protectedInterface();}    // ok!
void learnPrivate(){privateInterface();}        // not compiling
};
class TA : public Teacher {
public: void correctExercises(){publicInterface();}
};
class Child : public Student {
public: void play(){publicInterface();}  //NOT working, since we priva
};
int main()
{
    Person* p = new Person();       // ok is-a
Person* pro = new Professor();  // ok    is-a
    Person* t = new Teacher();      // not compiling! No is-a relat.
    Person* s = new Student();      // not compiling! No is-a
    Teacher* t2 = new Teacher();        // ok
    Student* s2 = new Student();    // ok
    pro->publicInterface();     // ok
t2->publicInterface();      // not compiling
s2->publicInterface();      // not compiling
t2->teachPublic();          // ok
t2->teachProtected();           // ok
t2->teachPrivate();         // not compiling
s2->learnPublic();          // ok
s2->learnProtected();           // ok! learnProteted is public
s2->learnPrivate();         // not compiling
TA* ta = new TA();          // ok 
Child* c = new Child();     // ok
ta->correctExercises(); // ok
c->play();              // ok however call inside play not ok!
}

But I still think this is quite obscure .. no one would really come up with such a design in real world, right?

Simon
  • 706
  • 6
  • 23
-2

The function Person::learnProtected() is not visible in the class Student, so when you declare Student::learnProtected(), the compiler understands it as not override declaration.

You could ensure this adding the constrain "override" to Student::learnProtected(), the compiler will show an error when the function is not overriding a parent virtual function:

public: void learnProtected() override { protectedInterface(); }

This causes the compiler error:

C3668 'Student::learnProtected': method with override specifier 'override' did not override any base class methods ConsoleApplication3