24

I am just confused about the tiny program on inheritance below:

#include<iostream>
using namespace std;

struct B {
    virtual int f() { return 1; }
}; // f is public in B

class D : public B { 
    int f() { return 2; }
}; // f is private in D

int main()
{
    D d;
    B& b = d;
    cout<<b.f()<<endl; // OK: B::f() is public, D::f() is invoked even though it's private
    cout<<d.f()<<endl; // error: D::f() is private
}
  1. I can't figure out why D::f() is private, D is public inherited from B, so the public function f in B
    is also public in D (I know without inheritance, member access is private by default)
  2. f is a virtual function in B, so if we call b.f(), we actually call D::f(), but just as the illustration mentioned, why D::f() is able to be invoked even though it's private?

Can anyone explain the simple inheritance problem in detail?

KernelPanic
  • 2,328
  • 7
  • 47
  • 90
Eric Luo
  • 339
  • 3
  • 11
  • 1
    *I can't figure out why D::f() is private* -- Because it is a `private` function. It certainly is not `public`. You have a `class`, you have no access specifier, the function is `private`. – PaulMcKenzie Aug 31 '16 at 03:05
  • So then the question is: Why can it still be called through the base class' function? Reasonable inference... – qxz Aug 31 '16 at 03:07
  • 3
    Having a member function of a derived class `private` does not turn off the virtual mechanism. and thankfully so. Many design patterns use this technique (template pattern, for example) where the derived class implements the virtual function as private (or protected). – PaulMcKenzie Aug 31 '16 at 03:10
  • I understand the default access specifier is private, but as D is **public** inherited from B, **public** function in B should also be **public** in D, where's my misunderstanding? – Eric Luo Aug 31 '16 at 03:12
  • You're example is calling `D` functions from outside of any class, not through `B`. – PaulMcKenzie Aug 31 '16 at 03:14
  • @EricLuo Does it help if I say "access protection in **not** a runtime attribute"? – Adrian Colomitchi Aug 31 '16 at 03:35
  • [Suddenly.](http://coliru.stacked-crooked.com/a/7353524a010bcea5) – Constructor Aug 31 '16 at 09:56
  • 1
    As a comparison, notice that you can't reduce visibility of inherited method in Java (so if a method is public in superclass, it cannot be made private in subclass). – Adam Kotwasinski Aug 31 '16 at 10:12
  • @EricLuo Because B::f is also private. – naslundx Aug 31 '16 at 14:44
  • @naslundx no, if you watch carefully, `B` is declared as `struct`, so `B::f` is public – Guillaume Racicot Aug 31 '16 at 18:27

7 Answers7

21

This has to do that with virtual dispatch is a runtime concept. The class B doesn't care which class extends it, and it doesn't care if it's private or public because it can't know.

I can't figure out why D::f() is private, D is public inherited from B, so the public function f in B is also public in D(I know without inheritance, member access is private by default)

D::f() is private because you made it private. This rule isn't affect by inheritance nor virtual dispatch.

f is a virtual function in B, so if we call b.f(), we actually call D::f(), but just as the illustration mentioned, why D::f() is able to be invoked even though it's private?

Because in reality, when invoking b.f(), the compiler has no idea which function will actually be called. It will simply call the function f(), and since B::f is virtual, the called function will be chosen at runtime. The runtime program has no information about which function is private or protected. It only know functions.

If the function is chosen at runtime, the compiler can't know at compile-time what function will be called, and the access specifier can't be known. In fact, the compiler won't even try to check if the function called will be private or not. The access specifier could be in some code that the compiler haven't seen yet.

As you've experienced, you can't call D::f directly. This is exactly what private will do: prohibit direct access of the member. You can, however, access it indirectly through a pointer or a reference. The virtual dispatch you use will do that internally.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
12

Access specifiers apply to the function name only, they aren't some restriction on how or when the function can be called by other means. A private function could be called outside the class if it is made available by some means other than its name (for example, a function pointer).

For a class declared with class keyword, the default access-specifier is private. Your code is the same as:

// ...
class D: public B
{
private:
  int f() { return 2; }
};

As you can see, f is private in D. It makes no difference what the access specifier was of any function in B with the same name. Be clear in your mind that B::f() and D::f() are two different functions.

The effect of the virtual keyword is that if f() without a scope qualifier is called on a B reference that refers to a D object, then even though it resolves to B::f(), actually D::f() is invoked instead.

This process still uses the access specifier for B::f(): access is checked at compile-time; but it might be run-time matter as to which function is called.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • I have a question, if no definition of f() is made in D, then is `D::f()` the same function as `B::f()`? is it public in `class D`? – Eric Luo Aug 31 '16 at 05:16
  • @EricLuo in that situation `D::f()` and `B::f()` are indeed the same function, and the access of the function via the name `D::f()` is determined by the inheritance rules. The exact text in the C++ Standard is, "Unless redeclared in the derived class, members of a base class are also considered to be members of the derived class." – M.M Aug 31 '16 at 05:25
  • finally I understand! One more request, would you mind tell me where the exact text is? – Eric Luo Aug 31 '16 at 05:38
  • @EricLuo [see here](http://stackoverflow.com/questions/81656/where-do-i-find-the-current-c-or-c-standard-documents) to find a standards draft (e.g. N4140, N3936) and then check section [class.derived]/2 – M.M Aug 31 '16 at 05:40
  • Or [class.access.virt] see my answer – TemplateRex Aug 31 '16 at 06:09
  • @M.M I'm confused again after seeing [a similar question](http://stackoverflow.com/questions/14270631/c-do-sub-classes-really-inherit-private-member-variables), if no definitions are made in inherited class, the functions are same, what about private members? Do changes of private members in base class(or super classes) affect the others? – Eric Luo Sep 01 '16 at 07:32
  • @EricLuo the function names and variable names behave in the same way – M.M Sep 01 '16 at 07:32
  • @EricLuo I wrote an answer to that question, maybe it will assist your understanding – M.M Sep 01 '16 at 08:13
6

The C++ Standard has an exact example of this:

11.5 Access to virtual functions [class.access.virt]

1 The access rules (Clause 11) for a virtual function are determined by its declaration and are not affected by the rules for a function that later overrides it. [Example:

class B { 
public:
  virtual int f();
};

class D : public B { 
private: 
  int f(); 
}; 

void f() { 
  D d; 
  B* pb = &d; 
  D* pd = &d; 
  pb->f();     // OK: B::f() is public, 
               // D::f() is invoked
  pd->f();     // error: D::f() is private
} 

-- end example]

Can't explain it any clearer.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • "The access rules for a virtual function..." is a bit of a red herring, the same rules apply to virtual and non-virtual functions. Take out `virtual` in this example and it is still the case that `pb->f();` is correct and `pd->f()` is not – M.M Aug 31 '16 at 06:12
  • @M.M it is a bit of a red herring re part 1 of the OP's question, but it is right on point for part 2, namely why `pb->f()` calls the private derived version. That will no longer work without virtual. – TemplateRex Aug 31 '16 at 06:14
5

The answer given illustrates what is being done, but why would you ever want to do this, where the base class calls private virtual functions?

Well, there is a design pattern called the template method pattern that uses this technique of having a base class that calls private virtual functions in the derived class.

struct B 
{
    virtual ~B() {};
    int do_some_algorithm()
    {
       do_step_1();
       do_step_2();
       do_step_3();
    }

    private:
          virtual void do_step_1() {}
          virtual void do_step_2() {}
          virtual void do_step_3() {}
};

class D : public B 
{ 
   void do_step_1() 
   {
      // custom implementation
   }
   void do_step_2() 
   {
      // custom implementation
   }

   void do_step_3() 
   {
      // custom implementation
   }
};

int main()
{
   D dInstance;
   B * pB = &dInstance;
   pB->do_some_algorithm();
}

This allows us to not expose the custom steps of class D to the public interface, but at the same time allows B to call these functions using a public function.

PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45
  • The example does not illustrate a use case for the OP question, because the virtual functions in `B` are private (as well as their implementation in `D`). From `main` the user couldn't call them in any way, neither through a virtual nor through a direct call. The thing to illustrate would be why it can be useful to forbid calling `D::f` directly, even though the user can circumvent that by casting the D object reference to a B reference, and then calling the `f` method virtually. – Marc van Leeuwen Aug 31 '16 at 14:09
3

This actually has less to do with virtual dispatch and more to do with what access specifiers mean.

The function itself is not private; its name is.

Consequently, the function cannot be named outside of the scope of the class, e.g. from main. However, you can still do so through a name that is public (i.e. the base's virtual function that is overridden) or from a scope in which the function's name is accessible despite the private qualifier (e.g. a member function of that class).

That's just how it works.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
1

why D::f() is able to be invoked even though it's private?

To understand virtual function mechanism it is good to know, how it is usually implemented. A function at runtime is actually no more than an address in memory where the executable code of the function body locates. To call the function we need to know its address (a pointer). C++ object with virtual functions representation in memory contains so called vtable - an array of pointers to the virtual functions.

vtable typical implementation

Key point is that in derived classes vtable repeats (and may extend) the vtable of base class, but if the virtual function is overriden its pointer is replaced in derived object's vtable.

When a virtual function call is done through the base class pointer, the address of the virtual function is calculated as an offset in the vtable array. No other checks are done, just the function address is taken. If it is a base class object, it will be the address of the base class function. If it is a derived class object, it will be the address of the derived class function, does not matter if it was declared private or not.

This how it works.

Spock77
  • 3,256
  • 2
  • 30
  • 39
  • How virtual dispatch is implemented is completely irrelevant! I'd say that, rather than obsessing over internals, it's far more valuable to learn how to think in abstractions. The C++ standard already has an abstraction ready-made for us, so we just need to restrict our language to its scope. :) – Lightness Races in Orbit Aug 31 '16 at 16:30
0

Member of struct is default to be public, and member of class is default to be private. So f() in B is public and when it's derived to D, because you didn't explicitly declare it is public, so according to rules of derivation, it became private.