《Effective C++》学习笔记——条款32

***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************


六、继承与面向对象设计
six、Inheritance and Object-Oriented Design



面向对象编程(OOP)几乎流行了两个年代,即使你过去只用C编程,现在也没办法逃脱这个趋势。
本章主要看 C++的OOP与你可能习惯的OOP的不同:
> "继承" 可以使单一继承或多重继承
> 每一个继承连接(link)可以是public,protected or private,也可以是 virtual or non-virtual
> 成员函数的各个选项:virtual?non-virtual?pure virtual?以及成员函数和其他语言特性的交互影响:缺省参数值与virtual函数有什么交互影响。
> 还有一些其他的,比如:继承如何影响C++的名称查找规则?设计选项有哪些?如果class的行为需要修改,virtual函数是最佳选择吗?

除了这些,本章还会解释C++各种不同特性的真正意义。比如:
> "public继承" 意味 "is-a"
> virtual函数 意味 "接口必须被继承";non-virtual函数 意味 "接口和实现都必须被继承"
等等



条款 32:确定你的public继承塑模出is-a关系
Rule 32:Make sure public inheritance models "is-a"



1.public继承 与 is-a
以C++进行面向对象编程,最重要的一个规则是:public inheritance(公开继承)意味"is-a"的关系
如果你令 class D("Derived")以public形式继承 class B("Base"),你便是告诉C++编译器(亦或是看你代码的人)——每一个类型为D的对象同时也是一个类型为B的对象,反之则不成立。
例如:

class Person  {  ...  };
class Student : public Person  {  ...  };

上面的代码说明——每个学生都是人,但并非每个人都是学生。这就是is-a。

在C++领域中,任何函数如果期望获得一个类型为Person(或pointer-to-Person或reference-to-Person)的实参,都也愿意接受一个Student对象(或pointer-to-Student或reference-to-Student):

void eat(const Person& p);    // 任何人都会吃
void study(const Student& s);    // 只有学生才到校学习

Person p;    // p是人
Student s;    // s是学生
eat(p);    // ok,p是人,可以执行吃这个动作
eat(s);    // ok,s是学生,根据is-a,可以执行吃这个动作
study(s);    // ok,s是学生,可以执行到校学习这个动作
study(p);    // no! p是人,并非每个人都可以执行到校学习这个动作

>这个论点只对public继承才成立< 对于private继承,会完全不同,详见条款39,至于protected继承,作者也没搞太明白。。。




2.产生的各种问题
>1 public继承和is-a之间的等价关系听起来非常简单,但有时候可能被误导,比如:企鹅是一种鸟,鸟可以飞,但如果我们用这样的形式来描述这种关系:

class Bird  {
public:
  virtual void fly();    // 鸟可以飞
  ...
};
class Penguin : public Bird  {    // 企鹅是一种鸟
  ...
};

Boom!发生了什么事情?我们的三观何在。。。
显然,这样是错误的,我们不得不承认有数种鸟不会飞。
但如何反应我们的意思呢?
① 就像下面这样的继承体系,更能准确反映出我们的意思:

class Bird  {    
  ...    // 没有声明fly函数
};
class FlyingBird : public Bird  {
public:
  virtual void fly();
  ...
};
class Penguin : public Bird  {
  ...
};

但是,此时这件事就结束了吗?No no no,因为对某些软件系统而言,可能不需要区分会飞的鸟和不会飞的鸟。假如,你的程序对鸟喙和鸟翅更加感兴趣,完全不在乎飞行,那么刚开始的"双class继承体系"或许就可以满足了。
> 这实际上是说明一个事实——世界上并不存在一个"适用于所有软件"的完美设计;所谓的最佳设计,取决于系统希望做什么事。 <
② 对于更加准确反映我们的想法,还有一种方法。为企鹅重新定义fly函数,令它产生一个运行期错误。

void error(const std::string& msg);
class Penguin : public Bird  {
public:
  virtual void fly()  {  error("Attempt to make a penguin fly!");  }
  ...
};

这里所说的某些东西可能和你所想的不同,这里并不是说“企鹅不会飞”,而是说“企鹅会飞,但尝试那么做是一种错误”!
③两者的差异,从错误被侦测出来的时间点来看:“企鹅不会飞”这一限制可由编译期强制实施,但若违反了“企鹅会飞,但尝试那么做是错误的”这条规则,只有运行期才能检测出来。
>2 class Square 应该以 public继承 class Rectangle 吗?
当然,学校是这么说的——正方形是一种特殊的长方形
但是,看看下面这段代码:

class Rectangle  {
public:
  virtual void setHeight(int newHeight);
  virtual void setWidth(int newWidth);
  virtual int height() const;    // 返回当前值
  virtual int width() const;
  ...
};
void makeBigger(Rectangle& r)
{
  int oldHeight = r.height();
  r.setWidth(r.width()+10);    // 为r的宽度加10
  assert(r.height()==oldHeight);    // 判断r的高度是否未曾改变
}

显然,上面的 assert结果永远为真。因为makeBigger只改变了r的宽度,r的高度从未被改变。
然后:

class Square : public Rectangle  {  ...  };
Square s;
...
assert(s.width()==s.height());    // 这对于所有的正方形一定为真
makeBigger(s);                    // 由于public继承,s是一种(is-a)矩形


assert(s.width()==s.height());    // 对所有的正方形应该仍然为真

这也很明显,第二个assert结果也应该永远为真。因为根据定义,正方形的高度和宽度相同。
But,我们如何调节下面这些assert判断式?
▪ 调用makeBigger之前,s的高度和宽度相同
▪ 在makeBigger函数内,s的宽度改变,但高度不变
▪ makeBigger返回之后,s的高度再度和其宽度相同。(注意,s是以by reference方式传给makeBigger,所以makeBigger修改的是s自身,不是s的副本)
其实,本例的根本困难是,某些可施行与矩形身上的事(例如宽度可独立于其高度被外界修改)却不可实行于正方形身上(宽度总应该与高度一样)。
但是,public继承主张,能够实行于base class对象身上的每件事情,每件事情也同时可以实行于derived class身上。
还有最最重要的一点:☆ 代码通过编译并不代表可以正常的运作 ☆ 





3.最后
is-a并非是唯一存在于class之间的关系。另两个常见的关系是has-a(有一个)和is-implemented-in-terms-of(根据实物实现出)。
将上述这些重要的相互关系中的任何一个误塑为is-a而造成的错误设计,在C++中并不罕见,所以你应该确定你确实了解这些个"class相互关系"之间的差异,并知道如何在C++中最好的塑造它们。




★请记住★
▪ "public继承"意味着is-a。适用于base class身上的每一件事情一定也适用于derived class身上,因为每一个derived class对象也都是一个base class对象。





***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************

发布了473 篇原创文章 · 获赞 391 · 访问量 78万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览