Why is it allowed to call derived class' priva

2019-01-23 05:55发布

问题:

# include <iostream>
using namespace std;

class A
{
    public:
    virtual void f()
    {
        cout << "A::f()" << endl;
    }
};
class B:public A
{
    private:
    virtual void f()
    {
        cout << "B::f()" << endl;
    }
};
int main()
{
    A *ptr = new B;
    ptr->f();
    return 0;
}

This code works correctly and prints B::f(). I know how it works, but why is this code allowed?

回答1:

Access control is performed at compile time, not runtime. There's no way in general for the call to f() to know the runtime type of the object pointed to by ptr, so there's no check on the derived class's access specifiers. That's why the call is permitted.

As for why class B is permitted to override using a private function at all - I'm not sure. Certainly B violates the interface implied by its inheritance from A, but in general the C++ language doesn't always enforce inheritance of interface, so the fact that it's Just Plain Wrong doesn't mean C++ will stop you.

So I'd guess that there's probably some use case for this class B - substitution still works with dynamic polymorphism, but statically B is not a substitute for A (e.g. there can be templates that call f, that would work with A as argument but not with B as argument). There may be situations where that's exactly what you want. Of course it could just be an unintended consequence of some other consideration.



回答2:

This code is allowed because f is public in A's interface. A derived class cannot change the interface of a base class. (Overriding a virtual method isn't changing the interface, nor is hiding members of a base, though both can appear to do so.) If a derived class could change a base's interface, it would violate the "is a" relationship.

If the designers of A want to make f inaccessible, then it should be marked protected or private.



回答3:

Your base class is defining the interface for all the inherited children. I do not see why it should prevent the mentioned access. You can try deriving a class down from 'B' and use the Base interface to call , which would result in an error.

Cheers!



回答4:

In addition to Steve's answer:

  • B is publically derived from A. That implies Liskov substitutability
  • Overriding f to be private seems to violate that principle, but actually it does not necessarily - you can still use B as an A without the code getting in the way, so if the private implementation of f is still okay for B, no issues
  • You might want to use this pattern is B should be Liskov substitutable for A, but B is also the root of another hierachy that is not really related (in Liskov-substitutable fashion) to A, where f is no longer part of the public interface. In other words, a class C derived from B, used through a pointer-to-B, would hide f.
  • However, this is actually quite unlikely, and it would probably have been a better idea to derive B from A privately or protected


回答5:

Function access control check happens in later stage of a c++ function call. The order in high level would be like name lookup, template argument deduction(if any), overload resolution, then access control(public/protect/private) check.

But in your snippet, you were using a pointer to base class and function f() in base class is indeed public, that's as far as compiler can see at compiler time, so compiler will certain let your snippet pass.

A *ptr = new B;
ptr->f();

But all those above are happens at compile time so they are really static. While virtual function call often powered by vtable & vpointer are dynamic stuff which happens at runtime, so virtual function call is orthogonal to access control(virtual function call happens after access control),that's why the call to f() actually ended B::f() regardless is access control is private.

But if you try to use

B* ptr = new B;
ptr->f()

This will not pass despite the vpointer & vtable, compiler will not allow it to compile at compile time.

But if you try:

B* ptr = new B;
((static_cast<A*>(ptr))->f();

This would work just fine.



回答6:

Pretty much like in Java, in C++ you can increase the visibility of methods but not decrease it.