简体   繁体   English

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

[英]Overriding public virtual functions with private functions in C++

Is there is any reason to make the permissions on an overridden C++ virtual function different from the base class? 是否有任何理由使重写的C ++虚函数的权限与基类不同? Is there any danger in doing so? 这样做有危险吗?

For example: 例如:

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

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

The C++ faq says that it is a bad idea, but doesn't say why. C ++ faq说这是一个坏主意,但没有说明原因。

I have seen this idiom in some code and I believe that the author was attempting to make the class final, based on an assumption that it is not possible to override a private member function. 我已经在一些代码中看到了这个习惯用法,我相信作者试图让这个类最终,基于一个假设,即不可能覆盖私有成员函数。 However, This article shows an example of overriding private functions. 但是, 本文显示了重写私有函数的示例。 Of course another part of the C++ faq recommends against doing so. 当然,C ++ faq的另一部分建议不要这样做。

My concrete questions: 我的具体问题:

  1. Is there any technical problem with using a different permission for virtual methods in derived classes vs base class? 在派生类和基类中使用不同的虚拟方法权限是否存在任何技术问题?

  2. Is there any legitimate reason to do so? 有没有合理的理由这样做?

You do get the surprising result that if you have a child, you can't call foo, but you can cast it to a base and then call foo. 你确实得到了令人惊讶的结果,如果你有一个孩子,你不能打电话给foo,但你可以把它扔到基地,然后打电话给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

I suppose you might be able to contrive an example where you don't want a function exposed, except when you are treating it as an instance of the base class. 我想你可以设想一个你不想暴露函数的例子,除非你把它当作基类的一个实例。 But the very fact that that situation pops up would suggest a bad OO design somewhere along the line that should probably be refactored. 但是,这种情况突然出现的事实表明,在可能被重构的线路上的某个地方有一个糟糕的OO设计。

The problem is that the Base class methods are its way of declaring its interface. 问题是Base类方法是声明其接口的方式。 It is, in essence saying, "These are the things you can do to objects of this class." 从本质上讲,它是“你可以对这类对象做的事情。”

When in a Derived class you make something the Base had declared as public private, you are taking something away. 在Derived类中,你创建了Base已声明为public private的东西,你正在拿走一些东西。 Now, even though a Derived object "is-a" Base object, something that you should be able to do to a Base class object you cannot do to a Derived class object, breaking the Liskov Substitution Prinicple 现在,即使Derived对象是“is-a”Base对象,你应该能够对Base类对象做一些你无法对Derived类对象做的事情,打破了Liskov Substitution Prinicple

Will this cause a "technical" problem in your program? 这会在你的程序中引起“技术”问题吗? Maybe not. 也许不吧。 But it will probably mean object of your classes won't behave in a way your users expect them to behave. 但它可能意味着您的类的对象不会以用户期望它们的行为方式运行。

If you find yourself in the situation where this is what you want (except in the case of a deprecated method referred to in another answer), chances are you have an inheritance model where inheritance isn't really modeling "is-a," (eg Scott Myers's example Square inheriting from Rectangle, but you can't change a Square's width independent of its height like you can for a rectangle) and you may need to reconsider your class relationships. 如果你发现自己处于你想要的状态(除了在另一个答案中提到的弃用方法),你可能有一个继承模型,其中继承不是真正建模“is-a”(例如,斯科特迈尔斯的例子Square继承自Rectangle,但是你无法改变Square的宽度而不像它的高度那样,你可能需要重新考虑你的阶级关系。

There's no technical problem, but you will end up with a situation where the publicly available functions will depend upon whether you have a base or derived pointer. 没有技术问题,但最终会出现公共可用功能取决于您是否具有基指针或派生指针的情况。

This in my opinion would be weird and confusing. 在我看来,这将是奇怪和混乱。

如果您使用私有继承,那么它非常有用 - 即您希望重用基类的(自定义)功能,而不是接口。

It can be done, and very occasionally will lead to benefits. 它可以做到,偶尔也会带来好处。 For example, in our codebase, we are using a library that contains a class with a public function that we used to use, but now discourage the use of due to other potential problems (there are safer methods to call). 例如,在我们的代码库中,我们使用的库包含一个我们以前使用的具有公共函数的类,但现在不鼓励使用其他潜在的问题(有更安全的方法可以调用)。 We also happen to have a class derived from that class which a lot of our code uses directly. 我们碰巧有一个派生自该类的类,我们的许多代码直接使用它。 So, we made the given function private in the derived class in order to help everyone remember not to use it if they can help it. 因此,我们在派生类中将给定函数设为私有,以帮助每个人记住如果他们可以帮助它就不要使用它。 It doesn't eliminate the ability to use it, but it will catch some uses when the code tries to compile, rather than later in the code reviews. 它并没有消除使用它的能力,但它会在代码尝试编译时捕获一些用途,而不是在代码审查的后期。

A good use-case for private inheritance is a Listener/Observer event interface. 私有继承的一个很好的用例是Listener / Observer事件接口。

Example code for the private object: 私有对象的示例代码:

class AnimatableListener {
  public:
    virtual void Animate(float delta_time);
};

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

Some users of the object want the parent functionality and some want the child functionality: 该对象的某些用户想要父功能,而有些用户想要子功能:

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

This way the AnimationList can hold a reference to the parents and uses parent functionality. 这样, AnimationList可以保存对父项的引用并使用父功能。 While the Seal holds a reference to the child and uses the unique child functionality and ignoring the parent's. 虽然Seal持有对孩子的引用并使用独特的子功能并忽略父母的功能。 In this example, the Seal shouldn't call Animate . 在此示例中, Seal不应调用Animate Now, as mentioned above, Animate can be called by casting to the base object, but that's more difficult and generally shouldn't be done. 现在,如上所述,可以通过强制转换到基础对象来调用Animate ,但这样做更困难,通常不应该这样做。

  1. No technical problem, if you mean by technical as there being a hidden runtime cost. 没有技术问题,如果你的意思是技术,因为存在隐藏的运行时成本。
  2. If you inherit base publically, you shouldn't do this. 如果您公开继承基础,则不应该这样做。 If you inherit via protected or private, then this can help prevent using methods that don't make sense unless you have a base pointer. 如果你通过protected或private继承,那么这可以帮助防止使用没有意义的方法,除非你有一个基指针。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM