effective C++ 第六章
OOP:面向对象编程。
该章讲的是继承与面向对象设计
条款32:确定你的public 继承塑模出 is-a关系
一个重要规则是公开继承意味着一种关系。
is-a并非是唯一存在于classes之间的关系。另两个常见的关系是has-a和is-implemented-in-terms-of
public继承意味着is-a。适用于base classe s身上的每一件事也一定适用于derived classes身上,因为每一个derived class对象也都是一个base class对象。
条款33:避免遮掩继承而来的名称
derived classes内的名称会遮掩base classes内的名称。在public继承下从来没有人希望如此。
为了让被遮掩的函数重见天日,可使用using 声明式或转交函数
class Base{ public: virtual void mf1()=0; virtual void mf1(int); ......... }; class Derived:private Base{ public: virtual void mf1()//转交函数 { Base::mf1();}//暗自成为inline ...... }; Derived d; int x; d.mf1();//很好,调用的是Derived::mf1 d.mf1(x);//错误!Base::mf1()被遮掩了
条款34:区分接口继承和实现继承
public继承经过更严密的检查之后,发现它由两部分组成:函数接口继承和函数实现继承。
class Shape{ public: virtual void draw() const= 0; virtual void error (const std::string& msg); int objectID() const; ..... }; class Rectangle:public Shape{...}; class Ellipse:public Shape{....}; 纯虚函数有两个最突出的特性:他们必须被任何继承了他们的具象class重新声明,而且他们在抽象class中通常没有定义。你就会明白:
声明一个纯虚函数是为了让子类只继承函数接口。
这对Shape::draw函数是再合理不过的事了,因为所有Shape对象都应该是可绘出的,这是合理的要求。但Shape class 无法为此函数提供合理的缺省实现,毕竟椭圆绘法迥异于矩形绘法Shape::draw的声明式乃是对具象子类设计者说:“你必须提供一个draw函数,但我不干涉你怎么实现它”
Shape* ps=new Shape;//错误!Shape是抽象的 Shape* ps1=new Rectangle;//没问题 ps1->draw();//调用Rectangle::draw() Shape* ps2=new Ellipse;//没问题 ps2->draw();//调用Ellipse::draw ps1->Shape::draw();//调用Shape::draw() ps2->Shape::draw();//调用Shape::draw()
非纯虚函数和虚函数不同,一如往常,子类继承其函数接口,但非纯虚函数会提供一份实现代码,derived classes可能会覆写它。
声明纯虚函数的目的,是让子类继承该函数接口和缺省实现。
class Shape{ public: virtual void error (const std::string&msg); .... };
其接口表示,每个class都必须支持一个“当遇上错误时可调用”的函数,但每个class可自由处理错误。如果某个class不想针对错误做出任何特殊行为,它可以退回到Shape class 提供的缺省错误处理行为
但是,允许非纯虚函数同时指定函数声明和函数缺省行为,却有可能造成危险!
假设XYZ航空公司设计的飞机继承体系,有A型和B型飞机两者都以相同方式飞行。因此XYZ设计这样的继承体系
非虚函数具体指定接口继承以及强制性实现继承