Overriding public virtual functions with private functions in C++
是否有任何理由对重写的C++虚拟函数的权限与基类不同?这样做有危险吗?
例如:
1 2 3 4 5 6 7 8 9 | class base { public: virtual int foo(double) = 0; } class child : public base { private: virtual int foo(double); } |
C++ FAQ说这是个坏主意,但并没有说明原因。
我在一些代码中看到过这个习语,我认为作者正试图使类成为最终的,基于这样一个假设,即不可能重写私有成员函数。然而,本文展示了一个覆盖私有函数的例子。当然,C++ FAQ的另一部分建议不要这么做。
我的具体问题:
对于派生类和基类中的虚拟方法,使用不同的权限是否存在技术问题?
有什么正当理由这样做吗?
你会得到一个令人惊讶的结果,如果你有一个孩子,你不能称之为foo,但是你可以将它投射到一个基上,然后再称之为foo。
1 2 3 | child *c = new child(); c->foo; // compile error (can't access private member) static_cast<base *>(c)->foo(); // this is fine, but still calls the implementation in child |
我想您可以设计一个不希望函数公开的示例,除非您将它作为基类的实例来处理。但是,这种情况突然出现的事实表明,一个糟糕的OO设计可能会被重构。
问题是,基类方法是它声明接口的方法。它本质上是说,"这些是你可以对这个类的对象做的事情。"
在派生类中,当您使基声明为public private时,您将带走一些内容。现在,即使派生对象"is-a"是基对象,也应该能够对基类对象执行某些操作,而不能对派生类对象执行这些操作,从而打破了liskov替换原则
这会导致你的程序出现"技术"问题吗?也许不是。但这可能意味着类的对象的行为不会像用户期望的那样。
如果你发现自己处于这样一种情况下,你想要的就是这样(除了在另一个答案中提到的不推荐使用的方法),很可能你有一个继承模型,其中继承并不是真正建模的"is-a",(例如Scott Myers的示例平方继承自矩形,但是你不能改变一个独立于它的平方的宽度s的高度(就像矩形的高度一样),您可能需要重新考虑类关系。
没有技术问题,但最终会出现一种情况,即公开可用的函数将取决于您是否有基指针或派生指针。
在我看来,这将是奇怪和混乱的。
如果您使用私有继承,那么它可能非常有用——即,您希望重用(自定义)基类的功能,而不是接口。
这是可以做到的,而且偶尔也会带来好处。例如,在我们的代码库中,我们使用的库包含一个具有我们以前使用的公共函数的类,但由于其他潜在问题(有更安全的方法可调用),现在不鼓励使用。我们还碰巧有一个类派生自我们的许多代码直接使用的那个类。因此,我们在派生类中将给定的函数设为私有,以便帮助每个人记住,如果他们能够帮助的话,就不要使用它。它并没有消除使用它的能力,但是当代码试图编译时,它会捕捉到一些使用,而不是稍后在代码检查中。
私有继承的一个好的用例是侦听器/观察者事件接口。
私有对象的示例代码:
1 2 3 4 5 6 7 8 9 10 11 | class AnimatableListener { public: virtual void Animate(float delta_time); }; class BouncyBall : public AnimatableListener { public: void TossUp() {} private: void Animate(float delta_time) override { } }; |
对象的某些用户需要父功能,而有些用户需要子功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class AnimationList { public: void AnimateAll() { for (auto & animatable : animatables) { // Uses the parent functionality. animatable->Animate(); } } private: vector<AnimatableListener*> animatables; }; class Seal { public: void Dance() { // Uses unique functionality. ball->TossUp(); } private: BouncyBall* ball; }; |
这样,