Reputation: 1054
Following code prints "I'm B!". It's a bit strange because B::foo()
is private. About A* ptr
we can say that its static type is A
(foo
is public) and its dynamic type is B
(foo
is private). So I can invoke foo
via pointer to A
. But this way I have access to private function in B
. Can it be considered as encapsulation violation?
Since access qualifier is not part of class method signature it can lead to such strange cases. Why does in C++ access qualifier is not considered when virtual function is overridden? Can I prohibit such cases? What design principle is behind this decision?
#include <iostream>
class A
{
public:
virtual void foo()
{
std::cout << "I'm A!\n";
};
};
class B: public A
{
private:
void foo() override
{
std::cout << "I'm B!\n";
};
};
int main()
{
A* ptr;
B b;
ptr = &b;
ptr->foo();
}
Upvotes: 5
Views: 391
Reputation: 2036
You have multiple questions, so I'll try to answer them one-by-one.
Because access qualifiers are taken into account by the compiler after all overload resolutions. Such behavior is prescribed by the Standard.
For example, see on cppreference:
Member access does not affect visibility: names of private and privately-inherited members are visible and considered by overload resolution, implicit conversions to inaccessible base classes are still considered, etc. Member access check is the last step after any given language construct is interpreted. The intent of this rule is that replacing any private with public never alters the behavior of the program.
The next paragraph described the behavior demonstrated by your example:
Access rules for the names of virtual functions are checked at the call point using the type of the expression used to denote the object for which the member function is called. The access of the final overrider is ignored.
Also see the sequence of actions listed in this answer.
No.
And I don't think you will ever be able to do so, because there's nothing illegal in this behavior.
Just to clarify: by "decision" here I imply the prescription for the compiler to check the access qualifiers after overload resolution. The short answer: to prevent surprises when you're changing your code.
For more details let's assume you're developing some CoolClass
which looks like this
class CoolClass {
public:
void doCoolStuff(int coolId); // your class interface
private:
void doCoolStuff(double coolValue); // auxiliary method used by the public one
};
Assume that compiler can do overload resolution based on public/private specifiers. Then the following code would successfully compile:
CoolClass cc;
cc.doCoolStuff(3.14); // invokes CoolClass::doCoolStuff(int)
// yes, this would raise the warning, but it can be ignored or suppressed
Then at some point you discover that your private member function is actually useful for the class client and move it to "public" area. This automatically changes the behavior of the preexisting client code, since now it invokes CoolClass::doCoolStuff(double)
.
So the rules of applying access qualifiers are written in a manner that does not allow such cases, so instead you will get the "ambiguous call" compiler error in the very beginning. And virtual functions are no special case for the same reason (see this answer).
Not really. By converting pointer to your class into a pointer to its base class you're actually saying: "Herewith I would like to use this object B as if it's an object A" - which is perfectly legal, because the inheritance implies "as-is" relation.
So the question is rather, can your example be considered as violating contract prescribed by the base class? It seems that yes, it can.
See the answer to this question for alternative explanation.
Don't get me wrong, all this doesn't mean at all that you shouldn't use private virtual functions. On the contrary, it's often considered as a good practice, see this thread. But they should be private from the very base class. So again, the bottom line is, you should not use private virtual functions to break public contracts.
P.P.S. ...unless you deliberately want to force client to use your class via the pointer to interface / base class. But there are better ways for that, and I believe the discussion of those lies beyond the scope of this question.
Upvotes: 3
Reputation: 8018
Access qualifiers like public
, private
, etc. are a compile time feature, while dynamic polymorphism is a runtime feature.
What do you think should happen at runtime when a private
override of a virtual
function is called? An exception?
Can it be considered as encapsulation violation?
No, since the interface is already published through the inheritance, it isn't.
It's perfectly fine (and might be intended), to override a public virtual
function from the base class with a private
function in the derived class.
Upvotes: 2