在阅读了关于公共(public)继承的“Effective C++”部分后,我发现这个问题非常有趣。在我说是之前,这是常识,因为每个方块 是 矩形,但不一定是其他方式。但是请考虑以下代码:
void makeBigger(Rectangle& r) {
r.setWidth(r.width() + 10);
}
此代码非常适合
Rectangle
,但会破坏 Square
如果我们将对象传递给 makeBigger
- 它的双方将变得不平等。那么我该如何处理呢? 这本书没有提供答案(还没有?),但我正在考虑解决这个问题的几种方法:
setWidth()
和 setHeight()
Square
中的方法类(class)也调整了对方。缺点:代码重复,不需要
Square
的2个成员. Square
不继承自 Rectangle
并独立 - 拥有 size
, setSize()
等等。缺点:奇怪 - 正方形毕竟是矩形 - 重用会很好
Rectangle
的特征,如直角等。Rectangle
抽象(通过给它一个纯虚拟析构函数并定义它)并有第三个类来表示不是正方形的矩形并继承自 Rectangle
.这将迫使我们将上述函数的签名更改为:void makeBigger(NotSquare& r);
除了有额外的类(class)外,看不出任何缺点。
有没有更好的办法?我倾向于第三种选择。
最佳答案
这是我发现处理不当的面向对象设计的关键原则之一。迈耶先生在讨论您所指的这本书方面做得非常出色。
诀窍是记住这些原则必须应用于具体的用例。使用继承时,请记住,关键是当您想将该对象用作...时,“is a”关系适用于该对象,因此正方形是否为矩形取决于您将要做什么将来有矩形。
如果您将独立设置矩形的宽度和高度,那么不,正方形不是矩形(在您的软件上下文中),尽管它是数学上的。因此,您必须考虑将如何处理基础对象。
在您提到的具体示例中,有一个规范的答案。如果让 makeBigger 成为矩形的虚成员函数,那么每个函数都可以以适合类的方式进行缩放。但是,如果适用于矩形的所有(公共(public))方法都适用于正方形,那么这只是好的 OO 设计。
因此,让我们看看这如何适用于您迄今为止的努力:
如果正方形和矩形具有共享代码(例如,根据它们具有直角的事实计算面积),则可以通过组合来实现(每个都包含一个公共(public)类)。在这个简单的例子中,你可能最好使用一个函数,例如:
compute_area_for_rectangle(Shape* s){return s.GetHeight() * s.GetWidth());}
在命名空间级别提供。
因此,如果 Square 和 Rectangle 都继承自 Shape 基类,Shape 具有以下公共(public)方法:draw()、scale()、getArea() ...,所有这些对于任何形状都具有语义意义,并且通用公式可以通过命名空间级函数共享。
关于 oo 设计的观点:正如 icbytes 提到的,如果你打算有第三个类,这个类是一个有意义地表达常见用途的公共(public)基础更有意义。形状还可以。如果主要目的是绘制对象,那么 Drawable 可能是另一个好主意。
你表达这个想法的方式还有其他一些缺陷,这可能表明你对虚拟析构函数有误解,以及抽象意味着什么。每当您将类的方法设为 virtual 以便另一个类可以覆盖它时,您也应该将析构函数声明为 virtual (S.M. 确实在 Effective C++ 中讨论了这一点,所以我想您会自己发现这一点)。这并不使它抽象。当您声明至少一种纯虚拟方法时,它变得抽象 - 即没有实现
虚拟无效 foo() = 0;//例如
这意味着有问题的类不能被实例化。显然,因为它至少有一个虚方法,它也应该有声明为虚的析构函数。
我希望这有帮助。请记住,继承只是一种可以重用代码的方法。好的设计来自所有方法的最佳组合。
为了进一步阅读,我强烈推荐 Sutter 和 Alexandrescu 的“C++ 编码标准”,尤其是关于类设计和继承的部分。第34条“优先组合而不是继承”和第37条“公共(public)继承是可替代性。继承,不是重用,而是被重用。”
关于c++ - 类 Square 应该从类 Rectangle 公开继承吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18888693/