用C ++中的私有函数覆盖公共虚函数

是否有任何理由使重写的C ++虚函数的权限不同于基类? 这样做有没有危险?

例如:

class base { public: virtual int foo(double) = 0; } class child : public base { private: virtual int foo(double); } 

C ++常见问题解答说,这是一个坏主意,但没有说明原因。

我已经在一些代码中看到了这个习惯用法,我相信作者试图让这个类最终成为一个假设,即不可能重写一个私有成员函数。 但是, 这篇文章显示了重写私有函数的一个例子。 当然,C ++ faq的另一部分build议不要这样做。

我的具体问题:

  1. 在派生类和基类中使用不同的虚拟方法权限是否有任何技术问题?

  2. 有没有合法理由这样做?

问题是Base类方法是声明它的接口的方法。 实质上是说:“这些是你可以对这个class的对象做的事情。”

当你在一个派生类中做了一些基地宣布为公共私人的东西的时候,你正在带走一些东西。 现在,尽pipeDerived对象“is-a”是一个Base对象,您应该可以对Base类对象做某些事情,但是您无法对Derived类对象执行此操作,从而打破了LiskovreplacePrinted

这会在你的程序中造成“技术”问题吗? 也许不会。 但是这可能意味着你的类的对象不会以你的用户期望它们的行为方式行事。

如果你发现自己处于这种情况下(除了在另一个答案中提到的被弃用方法的情况),你有可能有一个inheritance模型,inheritance不是真正build模“is-a”(例如Scott Myers的Square从Rectangleinheritance的例子,但是你不能改变Square的宽度而不pipe它的高度,就像你可以为一个矩形一样),你可能需要重新考虑你的类关系。

你会得到惊人的结果,如果你有一个孩子,你不能叫foo,但你可以把它投到一个基地,然后打电话给富。

 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 

我想你可能可以devise一个例子,你不想暴露一个函数,除非你把它当作基类的一个实例。 但是事实上,这种情况会popup来,这意味着一个糟糕的OOdevise可能会被重构。

没有技术上的问题,但是最终会出现这样一种情况,即公开可用的函数将取决于您是否拥有基础指针或派生指针。

这在我看来会很奇怪和混乱。

这可以做到,偶尔会带来好处。 例如,在我们的代码库中,我们正在使用一个包含一个带有公用函数的类的库,但是由于其他潜在的问题(有更安全的方法可以调用),现在不鼓励使用它。 我们也碰巧有一个从我们的许多代码直接使用的类派生的类。 所以,我们在派生类中使给定的函数是私有的,以便帮助大家记住如果可以帮助的话就不要使用它。 它并没有消除使用它的能力,但是当代码试图编译时,它会捕获一些用途,而不是在代码评论的后面。

如果你正在使用私有inheritance – 也就是说你想重用一个基类的(自定义的)function,而不是接口,那么这会非常有用。

  1. 没有技术问题,如果你的技术意味着隐藏的运行成本。
  2. 如果你公开inheritance基地,你不应该这样做。 如果你通过保护或私有inheritance,那么这可以帮助防止使用无效的方法,除非你有一个基指针。

一个很好的私有inheritance用例是一个Listener / Observer事件接口。

私有对象的示例代码:

 class AnimatableListener { public: virtual void Animate(float delta_time); }; class BouncyBall : public AnimatableListener { public: void TossUp() {} private: void Animate(float delta_time) override { } }; 

该对象的一些用户需要父function,有些则希望子function:

 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; }; 

这样, AnimationList可以持有对父项的引用并使用父项function。 虽然Seal提到了孩子,并使用独特的儿童function,并忽略了父母的function。 在这个例子中, Seal不应该叫Animate 。 现在,如上所述,可以通过投射到基础对象来调用Animate ,但这更困难,通常不应该这样做。