第六章:继承与面向对象设计
条款32:确定你的public继承塑模出is-a关系
条款33:避免遮掩继承而来的名称
- 基类函数被子类函数所覆盖:
class Base{
private:
int x;
public:
virtual void f1() = 0;
virtual void f1(int);
virtual void f2();
void f3();
void f3(double);
...
}
class Derived: public Base{
public:
virtual void f1();
void f3();
void f4();
}
Derived d;
int x;
...
d.f1(); //没问题,调用Derived::f1();
d.f1(x); //错误,因为Derived::f1遮掩了Base::f1
d.f2(); //没问题,调用Base::f2()
d.f3(); //没问题,调用Derived::f3
d.f3(x); //错误,因为Derived::f3遮掩了Base::f3
- 解决方案:使用using声明
class Derived: public Base{
public:
using Base::f1;
using Base::f3;
virtual void f1();
void f3();
void f4();
}
//这样子在调用的时候,会先在子类的函数中寻找合适的,如果没有,则会在基类中寻找。
条款34:区分接口继承和实现继承
class Shape{
public:
virtual void draw() const = 0;
virtual void error(const striing& msg);
int objectID() const;
...
};
-
声明一个pure virtual函数的目的是为了让derived class只继承函数接口。如:
virtual void draw() const = 0; -
声明简朴的(非纯)impure virtual函数的目的。是让derived classes继承该函数的接口和缺省实现。如:
virtual void error(const striing& msg);
如果你不想实现自己的版本,那么也可以使用Base提供的缺省版本。 -
声明non-virtual函数的目的是为了令derived classes继承函数的接口及一份强制实现。这种函数绝不该在derived class中被重新定义
条款35:考虑virtual函数意外的其他选择
条款36:绝不重新定义继承而来的non-virtual函数
- 静态绑定:由指针的性质决定调用版本
class B{
public:
void mf();
...
};
class D: public B{
public:
void mf();
...
};
//
D x;
B* pb = &x;
pb->mf(); //调用B::mf
D* pd = &x;
pd->mf(); //调用D::mf
- 动态绑定:virtual 函数
无论是使用pb还是pd,真正指向的都是一个类型为D的对象。所以都会调用D::mf
条款37:绝不重新定义继承而来的缺省参数值
- virtual函数系动态绑定,而缺省参数值却是静态绑定。
class Shape{
public:
enum ShapeColor {Red, Green, Blue};
virtual void draw(ShapeColor color = Red) const = 0;
};
class Rectangle: public Shape{
public:
virtual void drwa(ShapeColor color = Green) const;
};
Shape* ps = new Rectangle;
ps->drwa(); //此时缺省参数是Red
缺省参数值来自Shape class而非Rectangle class!
条款38:通过符合塑模出has-a或根据某物实现出
条款39:明智而审慎地使用private继承
-
private继承意味只有实现部分被继承,接口部分应略去。如果D以private形式继承B,意思是D对象根据B对象实现而得。
-
尽可能使用复合,必要时才使用private继承。
-
private继承意味着is-implemented-in-terms of(根据某物实现出)。它通常会比复合(composition)的级别低。但是当derived class需要访问protected base class的成员,或需要重新定义继承而来的virtual函数时,private是合理的。
class Empty{}; //会占用一定空间,如一个char大小
条款40:明智而审慎地使用多重继承
-
virtual的代价:virtual 继承的那些classed所产生的对象往往比使用non-virtual继承的class体积要大,并且访问virtual base classed的成员函数变量时,也比访问non-virtual base classed的成员变量速度慢。
-
virtual base classes 使用技巧:
- 非必要不是用virtual bases,平常使用non-virtual继承
- 如果必须使用,尽可能避免在其中放置数据
-
多重继承会导致歧义。其使用场景之一是:public继承某个Interface class 和 private 继承某个协助实现的class的两相结合。