在 C++ 中用私有函数覆盖公共虚拟函数;

发布于 2024-07-12 23:55:38 字数 764 浏览 8 评论 0原文

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

例如:

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

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

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

我在一些代码中看到了这种习惯用法,并且我相信作者试图将类设为最终类,基于不可能重写私有成员函数的假设。 但是,本文展示了重写私有函数的示例。 当然,C++ 常见问题解答的另一部分推荐反对这样做。

我的具体问题:

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

  2. 这样做有什么正当理由吗?

Is there is any reason to make the permissions on an overridden C++ virtual function different from the base class? 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.

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.

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

小嗲 2024-07-19 23:55:39

你确实得到了令人惊讶的结果,如果你有一个孩子,你不能调用 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

我想您也许可以设计一个示例,在该示例中您不希望公开函数,除非您将其视为基类的实例。 但这种情况的出现本身就表明在某个地方存在着糟糕的面向对象设计,可能应该进行重构。

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.

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.

分開簡單 2024-07-19 23:55:39

问题在于基类方法是声明其接口的方式。 从本质上讲,它是在说“这些是您可以对此类对象执行的操作”。

当你在派生类中创建一些被基类声明为公有私有的东西时,你就拿走了一些东西。 现在,即使派生对象“是”基对象,您应该能够对基类对象执行的操作却无法对派生类对象执行,从而破坏了 里氏替换原理

这会导致您的程序出现“技术”问题吗? 也许不会。 但这可能意味着您的类的对象不会按照用户期望的方式运行。

如果您发现自己处于您想要的情况(除了另一个答案中提到的已弃用方法的情况),很可能您有一个继承模型,其中继承并不是真正建模“is-a”,(例如,Scott Myers 的示例 Square 继承自 Rectangle,但您无法像矩形那样独立于其高度来更改 Square 的宽度),并且您可能需要重新考虑您的类关系。

The problem is that the Base class methods are its way of declaring its interface. 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. 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

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," (e.g. 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.

国粹 2024-07-19 23:55:39

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

在我看来,这会很奇怪且令人困惑。

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.

想你只要分分秒秒 2024-07-19 23:55:39

如果您使用私有继承,它会非常有用 - 即您想要重用基类的(定制)功能,而不是接口。

It can be very useful if you are using private inheritance - i.e. you want to reuse a (customized) functionality of a base class, but not the interface.

杀お生予夺 2024-07-19 23:55:39

这是可以做到的,而且偶尔会带来好处。 例如,在我们的代码库中,我们正在使用一个库,其中包含一个具有公共函数的类,我们曾经使用过该函数,但现在由于其他潜在问题而不鼓励使用(有更安全的方法可以调用)。 我们还碰巧有一个从该类派生的类,我们的许多代码都直接使用它。 因此,我们在派生类中将给定的函数设为私有,以帮助每个人记住如果可以的话就不要使用它。 它并没有消除使用它的能力,但它会在代码尝试编译时(而不是稍后在代码审查中)捕获一些用途。

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.

轻许诺言 2024-07-19 23:55:39
  1. 没有技术问题,如果您所说的技术是指存在隐藏的运行时成本。
  2. 如果您公开继承基类,则不应该这样做。 如果您通过 protected 或 private 继承,那么这可以帮助防止使用没有意义的方法,除非您有基指针。
  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.
魄砕の薆 2024-07-19 23:55:39

私有继承的一个很好的用例是侦听器/观察者事件接口。

私有对象的示例代码:

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

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

该对象的一些用户想要父功能,而另一些用户想要子功能:

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 可以保存对父功能的引用并使用父功能。 而 Seal 保留对子级的引用并使用独特的子级功能并忽略父级的功能。 在此示例中,Seal 不应调用 Animate。 现在,如上所述,可以通过转换为基础对象来调用 Animate,但这更加困难,通常不应该这样做。

A good use-case for private inheritance is a Listener/Observer event interface.

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. While the Seal holds a reference to the child and uses the unique child functionality and ignoring the parent's. In this example, the Seal shouldn't call 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文