virtual breaks encapsulation in C++?
Here is the code,
Code:
class A
{
public:
virtual void foo(){}
};
class B : public A
{
private:
virtual void foo(){}
};
void main()
{
B b;
A* pA = &b;
pA->foo();
}
Obviously when you call pA->foo, it will call foo defined in B. But foo defined in B is private. It is not supposed to be called outside the class B. So it looks like virtual breaks encapsulation.
Re: virtual breaks encapsulation in C++?
Quote:
Originally Posted by
LarryChen
Obviously when you call pA->foo, it will call foo defined in B.
pA is not a pointer to B. It is declared as a pointer to A. At compile-time it is the static type that is considered, and the static type is "pointer to A".
Code:
class A
{
private:
virtual void foo(){}
};
class B : public A
{
public:
virtual void foo(){}
};
int main()
{
B b;
A* pA = &b;
pA->foo();
}
What results do you get with this change? I bet it doesn't even compile.
Regards,
Paul McKenzie
Re: virtual breaks encapsulation in C++?
Quote:
Originally Posted by
Paul McKenzie
[...]
What results do you get with this change? I bet it doesn't even compile.
I agree (though I didn't try it). I wouldn't even have expected the original code from post #1 to compile. And even if it's possible, I consider changing the visibility of virtual functions when deriving questionable in general.
In that original code, it's B that breaks its own encapsulation by making the private member function virtual, thereby allowing the base class (about which, ideally, it shouldn't make any assumptions beyond its public and protected interface) to call it. That's the decisiion of B's designer and he simply wouldn't need to do that if he wouldn't want to for some obscure reason. That's it.
After all, IMO private virtual functions don't make any sense anyway.
[EDIT:] Ok, one acceptable reason for a private virtual function I can imagine is to seal the function with respect to further derivates, i.e. disallowing them to override the function. And then it would be absolutely intended to allow the base class to call that function. I never encountered a design where I really would have wanted to do that, though.
Re: virtual breaks encapsulation in C++?
Sutter on public vs private virtual functions ...
http://www.gotw.ca/publications/mill18.htm
Re: virtual breaks encapsulation in C++?
Quote:
Originally Posted by
Eri523
one acceptable reason for a private virtual function I can imagine is to seal the function with respect to further derivates, i.e. disallowing them to override the function. And then it would be absolutely intended to allow the base class to call that function. I never encountered a design where I really would have wanted to do that, though.
Here is a scenario:
The base class has a public execute() function. This function must call virtual functions f1(), f2(), f3(), in that order. Also f1(), f2(), etc. are customized by the derived classes. How do you, at the very least, make it difficult to do the following:
a) call f1(), f2(), etc. from outside the classes, and
b) calling f1(), f2() in the wrong order of execution as specified by the requirements.
The way this is usually done is to declare f1(), f2(), etc. as private virtuals, and have execute() be a non-virtual public function in the base class. This way the derived classes still have customization, but the execution is controlled by the base class.
Regards,
Paul McKenzie
Re: virtual breaks encapsulation in C++?
Quote:
Originally Posted by LarryChen
Obviously when you call pA->foo, it will call foo defined in B. But foo defined in B is private. It is not supposed to be called outside the class B. So it looks like virtual breaks encapsulation.
The interface of B violates the Liskov substitution principle. However, the fact that only the type of pA is considered here saves the day, in a way.
Re: virtual breaks encapsulation in C++?
Quote:
Originally Posted by
Paul McKenzie
Here is a scenario:
The base class has a public execute() function. This function must call virtual functions f1(), f2(), f3(), in that order. Also f1(), f2(), etc. are customized by the derived classes. How do you, at the very least, make it difficult to do the following:
a) call f1(), f2(), etc. from outside the classes, and
b) calling f1(), f2() in the wrong order of execution as specified by the requirements.
The way this is usually done is to declare f1(), f2(), etc. as private virtuals, and have execute() be a non-virtual public function in the base class. This way the derived classes still have customization, but the execution is controlled by the base class.
Regards,
Paul McKenzie
Thanks for outlining that. When initially reading your post, I considered your example an extension of the Non-Virtual Interface idiom (that I've alreay used myself quite a few times), but after reading the excellent article Philip linked to in post #4 I concluded that it exactly is an application of the idiom.
Now I know my firm belief in private base class member functions being competely invisible to derived clases so they can't be overridden was a misconception that in fact I never questioned since it seemed so logical. So, of course, private virtual functions do make sense. However, most of my virtual functions probably will remain protected, as they've been all the time, since I frequently implement overrides in terms of the base class implentation, applying certain pre-filtering and/or post-processing.
The idea of making the use of the NVI idiom ones default implementation habit definitely seems to be worth another thought.
Re: virtual breaks encapsulation in C++?
Quote:
Originally Posted by
LarryChen
breaks encapsulation.
In my view this feature of C++ rather enhances encapsulation; A is the public interface and B is a hidden implementation of A not to be publicly instantiated.
It would be better if C++ sported a "module" feature but lacking that making all derived implementation classes all private is a good enougth alternative.
Re: virtual breaks encapsulation in C++?
Quote:
Originally Posted by
laserlight
The interface of B violates the Liskov substitution principle.
Well, B violates Liskov only if it's intended as a publicly exposed subtype of A. If the sole purpose of B is to be an anonymous implementation of A it doesn't.