类Square应该公开继承自Rectangle类吗? [英] Should class Square publicly inherit from class Rectangle?

查看:137
本文介绍了类Square应该公开继承自Rectangle类吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在阅读关于公共继承的Effective C ++部分之后,我发现这个问题非常有趣。之前我会说是的常识,因为每个方格都是矩形,但不一定是其他方式。但请考虑以下代码:

I find this question very interesting after reading the part of "Effective C++" about public inheritance. Before it would be common sense for me to say yes, because every square is a rectangle, but not necessarily other way around. However consider this code:

void makeBigger(Rectangle& r) { 

    r.setWidth(r.width() + 10); 

} 

此代码非常适合 Rectangle ,但是如果我们将它传递给 makeBigger ,它会破坏 Square 对象 - 双方会变得不平等。

This code is perfectly fine for a Rectangle, but would break the Square object if we passed it to makeBigger - its sides would become unequal.

那我怎么处理这个?这本书没有提供答案(但是?),但我想到了几种方法来解决这个问题:

So how can I deal with this? The book didn't provide an answer (yet?), but I'm thinking of a couple of ways of fixing this:


  1. 覆盖 setWidth() setHeight() Square 类中的方法也调整另一边。

  1. Override setWidth() and setHeight() methods in Square class to also adjust the other side.

缺点:代码重复, Square 不必要的2名成员。

Drawback: code duplication, unnecessary 2 members of Square.

对于 Square 不继承 Rectangle 并且独立 - 有 size setSize()等。

For Square not to inherit from Rectangle and be on its own - have size, setSize() etc.

缺点:怪异 - 正方形毕竟是矩形 - 重用矩形的特征,如直角等,这将是一件好事。

Drawback: weird - squares are rectangles after all - it would be nice to reuse Rectangle's features such as right angles etc.

使 Rectangle abstract(通过给它一个纯虚析构函数并定义它)并使第三类表示不是正方形的矩形继承自 Rectangle 。这将迫使我们将上述函数的签名更改为:

Make Rectangle abstract (by giving it a pure virtual destructor and defining it) and have a third class that represents rectangles that are not squares and inherits from Rectangle. That will force us to change the above function's signature to this:

void makeBigger(NotSquare& r);

除了额外的课程外,看不到任何缺点。

Can't see any drawbacks except having an extra class.






有更好的方法吗?我倾向于第三种选择。


Is there a better way? I'm leaning towards the third option.

推荐答案

这是OO设计中的一个关键原则,我发现它被错误处理。 Meyer先生非常好地讨论了你所指的那本书。

This is one of the key principles in OO design that I find gets handled incorrectly. Mr Meyer does an excellent job of of discussing it the book you are referring to.

诀窍是要记住这些原则必须适用于具体的用例。使用继承时,请记住关键是当您想要将该对象用作时,是一个关系适用于对象...因此,方块是否为矩形取决于你将来要做什么用矩形。

The trick is to remember that the principles must be applied to concrete use cases. When using inheritence, remember that the key is that the "is a" relationship applies to an object when you want to use that object as a ... So whether a square is a rectangle or not depends on what you are going to be doing with rectangles in the future.

如果你要独立设置一个矩形的宽度和高度,那么不,一个正方形不是一个矩形(在您的软件环境中)虽然它是数学上的。因此,你必须考虑你将对你的基础对象做什么。

If you will be setting width and height of a rectangle independently, then no, a square is not a rectangle (in the context of your software) although it is mathematically. Thus you have to consider what you will be doing with your base objects.

在你提到的具体例子中,有一个规范的答案。如果使makeBigger成为矩形的虚拟成员函数,则可以以适合类的方式缩放每个函数。但是,如果适用于矩形的所有(公共)方法都适用于正方形,那么这只是一个很好的OO设计。

In the concrete example you mention, there is a canonical answer. If you make makeBigger a virtual member function of rectangle, then each one can be scaled in a way that is appropriate to a class. But this is only good OO design if all the (public) methods which apply to a rectangle will apply to a square.

所以让我们看看到目前为止这对你的努力有何影响:

So let's see how this applies to your efforts so far:


  1. <我经常在生产代码中看到这种事情。在一个优秀的设计中修复差距是一种可能的错误,但这是不可取的。但这是一个问题,因为它导致代码在语法上是正确的,但在语义上是不正确的。它会编译并执行某些操作,但含义不正确。假设您正在迭代一个矩形向量,并将宽度缩放2,将高度缩放3.这对于正方形而言在语义上毫无意义。因此它违反了更喜欢编译时错误到运行时错误的规则。
  1. I see this kind of thing in production code pretty often. It's excusable as a kludge to fix a gap in an otherwise good design, but it is not desirable. But it's a problem because it leads to code which is syntactically correct, but semantically incorrect. It will compile, and do something, but the meaning is incorrect. Lets say you are iterating over a vector of rectangles, and you scale the width by 2, and the height by 3. This is semantically meaningless for a square. Thus it violates the precept "prefer compile time errors to runtime errors".

这里你考虑使用继承来重用代码。有一种说法是使用继承来重新使用,而不是重复使用。这意味着,您希望使用继承来确保oo代码可以在其他地方重新使用,作为其基础对象,而无需任何手动rtti。请记住,还有其他代码重用机制:在C ++中,这些机制包括函数式编程和组合。

Here you are thinking of using inheritance in order to re-use code. There's a saying "use inheritance to be re-used, not to re-use". What this means is, you want to use inheritance to make sure the oo code can be re-used elsewhere, as its base object, without any manual rtti. Remember that there other mechanisms for code re-use: in C++ these include functional programming and composition.

如果方形和矩形具有共享代码(例如,基于事实上,他们有正确的角度),你可以通过组合(每个包含一个共同的类)来做到这一点。在这个简单的例子中,你可能最好使用一个函数,例如:
compute_area_for_rectangle(Shape * s){return s.GetHeight()* s.GetWidth());}
名称空间级别

If square's and rectangles have shared code (e.g. computing the area based on the fact that they have right angles), you can do this by composition (each contains a common class). In this trivial example you are probably better off with a function though, for example: compute_area_for_rectangle(Shape* s){return s.GetHeight() * s.GetWidth());} provided at a namespace level.

因此,如果Square和Rectangle都继承自基类Shape,则Shape具有以下公共方法:draw(),scale(),getArea()...,所有这些对于任何形状都具有语义意义,并​​且通用公式可以通过命名空间级别函数共享。

So if both Square and Rectangle inherit from a base class Shape, Shape having the following public methods: draw(), scale(), getArea() ..., all of these would be semantically meaningful for whatever shape, and common formulas could be shared via namespace level functions.

我想如果你稍微冥想这一点,你会发现你的第三个建议有很多瑕疵。

I think if you meditate on this point a little, you'll find a number of flaws with your third suggestion.

关于oo设计观点:正如icbytes所提到的,如果你将要有第三个类,那么这个类是一个有意义表达的共同基础更有意义常见的用途。形状还可以。如果主要目的是绘制对象而不是Drawable可能是另一个好主意。

Regarding the oo design perspective: as icbytes mentioned, if you're going to have a third class, it makes more sense that this class be a common base that meaningfully expresses the common uses. Shape is ok. If the main purpose is to draw the objects than Drawable might be another good idea.

您表达这个想法的方式还有其他一些缺陷,这可能表明您对虚拟析构函数的误解,以及抽象的意义。每当你创建一个类的方法虚拟,以便另一个类可以覆盖它时,你应该声明析构函数是虚拟的(S.M。确实在Effective C ++中讨论它,所以我猜你会自己发现这个)。这并不是抽象的。当你声明至少一个纯粹虚拟的方法时,它变成抽象的 - 即没有实现

virtual void foo()= 0; //例如
这意味着无法实例化有问题的类。显然,因为它至少有一个虚方法,它也应该将析构函数声明为虚拟。

There are a couple other flaws in the way you expressed the idea, which may indicate a misunderstanding on your part of virtual destructors, and what it means to be abstract. Whenever you make a method of a class virtual so that another class may override it, you should declare the destructor virtual as well (S.M. does discuss this in Effective C++, so I guess you would find this out on your own). This does not make it abstract. It becomes abstract when you declare at least one of the methods purely virtual -- i.e. having no implementation
virtual void foo() = 0; // for example This means that the class in question cannot be instantiated. Obviously since it has at least one virtual method, it should also have the destructor declared virtual.

我希望有帮助。请记住,继承只是可以重用代码的一种方法。良好的设计源于所有方法的最佳组合。

I hope that helps. Keep in mind that inheritence is only one method by which code can be re-used. Good design comes out of the optimal combination of all methods.

为了进一步阅读,我强烈推荐Sutter和Alexandrescu的C ++编码标准,特别是关于类设计和继承的部分。第34项更喜欢继承权的构成和37公共继承权是可替代的。继承,不是重复使用,而是可以重复使用。

For further reading I highly recommend Sutter and Alexandrescu's "C++ Coding Standards", especially the section on Class Design and Inheritence. Items 34 "Prefer composition to inheritence" and 37 "Public inheritence is substitutability. Inherit, not to reuse, but to be reused.

这篇关于类Square应该公开继承自Rectangle类吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆