这是“Exceptional C ++”中的一个片段,第24项,解决方案,页面底部的第一个子弹:
永远不要使用公共继承来实现“IS-ALMOST-A”。我已经看到一些程序员,甚至是经验丰富的程序员,从基础公开继承并以保留基类语义的方式实现“大多数”被覆盖的虚函数。换句话说,在某些情况下,使用Derived对象作为Base将不会像合理的Base客户端所期望的那样运行。罗伯特·马丁经常引用的一个例子是从Rectangle类继承Square类通常被误导的想法“因为正方形是一个矩形”。这在数学中可能是正确的,但在课堂上并不一定如此。例如,假设Rectangle类具有虚拟SetWidth(int)函数。然后Square设置宽度的实现也会自然地设置高度,使对象保持方形。然而,在系统的其他地方可能存在与Rectangle对象一起使用多态的代码,并且不会期望更改宽度也会改变高度。毕竟,对于一般的矩形来说,情况并非如此!这是违反LSP的公共继承的一个很好的例子,因为派生类不提供与基类相同的语义。它违反了公共继承的关键原则:“不再需要,也不能承诺。”
我试过检查一下,然后写道:
// Square.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
using namespace std;
class Rectangle
{
private:
unsigned width_;
unsigned height_;
public:
Rectangle(const unsigned width, const unsigned height):width_(width),height_(height)
{/*Empty body*/ }
unsigned GetWidth()const
{
return width_;
}
unsigned GetHeight()const
{
return height_;
}
virtual void SetWidth(const unsigned width)
{
width_ = width;
}
void SetHeight(const unsigned height)
{
height_ = height;
}
virtual ~Rectangle()
{
cout << "~Rectangle()" << '\n';
};
};
class Square : public Rectangle
{
using Rectangle::SetWidth;
public:
Square(const unsigned width):Rectangle(width,width)
{
}
void SetWidth(const unsigned width)
{
SetWidth(width);
SetHeight(width);
}
~Square()
{
cout << "~Sqare()" << '\n';
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Rectangle** a = static_cast<Rectangle**>(operator new (sizeof(Rectangle) * 2));
a[0] = new Rectangle(10,10);
a[1] = new Square(5);
Rectangle* r = a[0];
cout << r->GetHeight() << "\t" << r->GetWidth() << '\n';
r = a[1];
cout << r->GetHeight() << "\t" << r->GetWidth() << '\n';
r = a[0];
r->SetWidth(20);//here I'm setting just width for a Rectangle
cout << r->GetHeight() << "\t" << r->GetWidth() << '\n';
delete a[1];
delete a;
return 0;
}
至于我从Rectangle继承Square是按预期工作的。那我在哪里弄错了,不明白这个子弹里说的是什么? 感谢
答案 0 :(得分:5)
关键是Square
类的语义与Rectangle
类的语义不同。假设你有一个像这样的通用实用函数:
void doubleArea(Rectangle &rect) {
rect.setWidth(rect.getWidth() * 2);
}
意图是对该函数的调用将使给定Rectangle的面积(宽度×高度)加倍。
使用派生的Square
课程,您现在可以执行以下操作:
Square sq(1);
doubleArea(sq);
突然sq
四次该区域,然后才打电话。你只打算将面积加倍,但得到了错误的结果。
由于Square
与Rectangle
没有完全相同的语义,因此有时对矩形执行的操作不适用于正方形。因此,通过从另一个派生Square
来声明Rectangle
是{{1}}并不是一个好主意,因为派生类不能满足基类所做的所有要求/承诺。
有关此主题的更多信息,请参阅C ++ FAQ Lite条目"Is a Circle a kind-of an Ellipse?"。
答案 1 :(得分:0)
你的代码很好。子弹的建议是有人可能会编写一些代码,这些代码取决于在SetWidth调用中保持不变的矩形高度:
int old_height = r->GetHeight();
r->SetWidth(100);
assert old_height == r->GetHeight();
在SetWidth
中实施Square
时,此代码将失败。