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?
是否有任何理由对覆盖的c++虚函数的权限与基类不同?这样做有危险吗?
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.
我在一些代码中见过这个习惯用法,我认为作者试图使类成为final,基于不可能覆盖私有成员函数的假设。但是,本文展示了重写私有函数的示例。当然,c++ faq的另一部分建议不要这样做。
My concrete questions:
我的具体问题:
-
Is there any technical problem with using a different permission for virtual methods in derived classes vs base class?
在派生类和基类中使用不同的虚拟方法权限有什么技术问题吗?
-
Is there any legitimate reason to do so?
有什么正当理由这样做吗?
7 个解决方案
#1
28
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
在派生类中,当你将基类声明为公有私有时,你就剥夺了一些东西。现在,即使一个派生对象“is-a”基对象,你也可以对基类对象做一些你不能对派生类对象做的事情,破坏Liskov替换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.
如果你发现自己在这种情况下这是你想要的东西(除了一个弃用方法被另一个答案),你可能有一个继承模型继承并不是建模“是一个”,(例如:斯科特·迈尔斯的例子广场继承矩形,但你不能改变一个正方形的宽度独立于它的高度像你可以为一个矩形),你可能需要重新考虑你的类关系。
#2
37
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,但是你可以将它转换为base然后调用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设计。
#3
6
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.
在我看来,这将是奇怪和令人困惑的。
#4
4
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.
如果您使用的是私有继承——也就是说,您希望重用基类的(定制的)功能,而不是接口,那么它将非常有用。
#5
3
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.
这是可以做到的,而且偶尔会带来好处。例如,在我们的代码库中,我们正在使用一个包含一个具有公共函数的类的库,但是由于其他潜在的问题(有更安全的方法可以调用),现在不鼓励使用这个库。我们也碰巧有一个类派生自那个类,我们的许多代码直接使用它。因此,我们在派生类中将给定的函数设置为私有,以便帮助每个人记住,如果可以的话,不要使用它。它并没有消除使用它的能力,但它将在代码试图编译时捕获一些使用,而不是在稍后的代码评审中。
#6
1
- No technical problem, if you mean by technical as there being a hidden runtime cost.
- 没有技术问题,如果你指的是技术问题,因为存在一个隐藏的运行时成本。
- 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继承,那么这可以帮助避免使用除非有基指针,否则没有意义的方法。
#7
1
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.
这样,AnimationList就可以保存对父类的引用并使用父类功能。当该密封保存对孩子的引用并使用唯一的子功能并忽略父类时。在这个例子中,密封不应该调用动画。如上所述,可以通过对基对象的强制调用来调用Animate,但是这比较困难,通常不应该这样做。
#1
28
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
在派生类中,当你将基类声明为公有私有时,你就剥夺了一些东西。现在,即使一个派生对象“is-a”基对象,你也可以对基类对象做一些你不能对派生类对象做的事情,破坏Liskov替换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.
如果你发现自己在这种情况下这是你想要的东西(除了一个弃用方法被另一个答案),你可能有一个继承模型继承并不是建模“是一个”,(例如:斯科特·迈尔斯的例子广场继承矩形,但你不能改变一个正方形的宽度独立于它的高度像你可以为一个矩形),你可能需要重新考虑你的类关系。
#2
37
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,但是你可以将它转换为base然后调用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设计。
#3
6
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.
在我看来,这将是奇怪和令人困惑的。
#4
4
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.
如果您使用的是私有继承——也就是说,您希望重用基类的(定制的)功能,而不是接口,那么它将非常有用。
#5
3
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.
这是可以做到的,而且偶尔会带来好处。例如,在我们的代码库中,我们正在使用一个包含一个具有公共函数的类的库,但是由于其他潜在的问题(有更安全的方法可以调用),现在不鼓励使用这个库。我们也碰巧有一个类派生自那个类,我们的许多代码直接使用它。因此,我们在派生类中将给定的函数设置为私有,以便帮助每个人记住,如果可以的话,不要使用它。它并没有消除使用它的能力,但它将在代码试图编译时捕获一些使用,而不是在稍后的代码评审中。
#6
1
- No technical problem, if you mean by technical as there being a hidden runtime cost.
- 没有技术问题,如果你指的是技术问题,因为存在一个隐藏的运行时成本。
- 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继承,那么这可以帮助避免使用除非有基指针,否则没有意义的方法。
#7
1
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.
这样,AnimationList就可以保存对父类的引用并使用父类功能。当该密封保存对孩子的引用并使用唯一的子功能并忽略父类时。在这个例子中,密封不应该调用动画。如上所述,可以通过对基对象的强制调用来调用Animate,但是这比较困难,通常不应该这样做。