3

In C++, it is possible to write an overriding for a base class's method even if the visibility declaration of the two don't match. What are the possible design considerations under the decision of not considering the visibility in the overriding rule?

Consider this piece of code as an example:

class A{
    public: virtual void f() { cout << "A::f" << endl; }
};

class B : public A {
    private: void f() { cout << "B::f" << endl; }
};

int main() {
    A* a = new B;
    a->f();
}

The above compiles in clang, and running it prints B::f, showing that it is possible to call a private function of B from outside the class, thus breaking encapsulation.

I don't really see why this type of behavior should be allowed. It is clearly not for performance/efficiency reasons, since checking statically that two visibility declarations match is trivial. Does anybody have an idea or hypothesis about what could possibly be the design decision behind this?

7

The above compiles in clang, and running it prints B::f, showing that it is possible to call a private function of B from outside the class, thus breaking encapsulation.

The encapsulation isn't broken. It's B that's semi-broken. B publicly inherits from A. A's public interface is B's public interface. The real question is why B should be permitted to even create a private override of a public function.

Simple fact is, there's just no real reason to ban it. If you're writing B and do so correctly, this should never happen in reality. Unless you explicitly intended it for some reason, in which case, congratulations.

  • 1
    A properly designed language should make all that's possible to avoid situations like this. "This will never happen unless the programmer is a moron" isn't a rule that neither a language nor its implementation should follow. – fsestini Jan 23 '16 at 21:01
  • 3
    @Phil: That is not how C++ is built. C++ will let you do things that don't necessarily make complete sense in some situations. By and large, if it's implementable, C++ will do it, and even then, it'll try to let you do things that aren't implementable. There's nothing actually incorrect about this situation, it's just not clear why anybody would ever do it. However, that's been the case for a lot of things that have turned out to be great, like expression templates. – DeadMG Jan 23 '16 at 21:33
  • so i guess it has to do with the overall C++ philosophy. I still don't get how A's public interface is B's public interface. You can call f() on A objects, but you cannot on B objects (or at least, on objects with static type B). – fsestini Jan 24 '16 at 13:07
2

For what it's worth, this actually has a semi-practical use. In the scenario you have set up, you can only use objects of type A or B via the interface defined by A. The function B::f is only callable via virtual dispatch, not directly.

For example:

B * pb = new B {};
A * pa = pb;
pa->f();       // fine, calls B::f via virtual dispatch
pa->A::f();    // fine, calls A::f directly
pa->B::f();    // error, B::f is private
pb->f();       // error, B::f is private
pb->A::f();    // fine, calls A::f directly
pb->B::f();    // error, B::f is private

You could use this to discourage people from depending on your implementation and rather program against the abstraction as they ought to. In practice, however, I'd prefer to hide the existence of B altogether and only provide a factory function that gives clients a polymorphic A of unspecified dynamic type.

1

It's absolutely intentional. Changing the visibility must (at most) change whether your code compiles or doesn't compile. It must never, ever change what the code does. If B::f() were public, then you would expect B::f() to be called. The fact that you made b::f() private cannot change this, according to the rule above; it is only allowed to change whether code compiles. Since the caller didn't even need to know about class B, the code must compile and call B::f().

There is no breaking of encapsulation here. In class A you declared that f is a virtual function which can be called by anyone through an instance of A. Subclasses cannot do anything to change that. If a developer thought that makeing B::f() private prevented the virtual function from being called, then that developer is just deluded.

  • 1
    My point is exactly that B::f, which is declared private, should not be considered a valid override, and the compiler should either raise a compile error or consider B::f as a simple redefinition (and putting A::f in B's vtable). I don't get it, I just see the choice of considering B::f an override completely arbitrary. – fsestini Jan 23 '16 at 21:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.