D
(Derived
)以 public 形式继承 class B
(Base
),你便是在告诉 C++ 编译器(亦或是看你代码的人):每一个类型为 D
的对象同时也是一个类型为 B
的对象,反之则不成立。B
(或 pointer-to-B 或 reference-to-B)的实参,都也愿意接受一个 D
对象(或 pointer-to-D 或 reference-to-D)。class Base {
public:
virtual void mf1() = 0;
virtual void mf1(int);
...
};
class Derived : private Base {
public:
virtual void mf1() // 转交函数,暗自成为inline(原因,见条款30)
{ Base::mf1(); }
...
};
...
Derived d;
int x;
d.mf1(); // 很好,调用的是Derived::mf1
d.mf1(x); // 错误! Base::mf1被遮掩了
class GameCharacter {
public:
virtual int healthValue() const;
...
};
这个方法,主张 virtual 函数应该几乎总是 private。
这个方法就是保留 healthValue
函数为 public,但让它成为 non-virtual,并间接调用一个 private virtual 函数:
class GameCharacter {
public:
int healthValue() const { // 派生类 不重新定义它
... // 做一些 其他工作
int retVal = doHealthValue();
...
return retVal;
}
...
private:
virtual int doHealthValue() const { // 派生类可以重新定义它
... // 缺省算法,可以重新定义
}
};
优点在于:在它做真正的事情前后都可以加入一些其他东西,就像上面定义体现的那样;NVI 方法允许派生类重新定义 virtual 函数,决定如何去实现,但基类有着何时被调用的权利。
缺点在于:这个方法让 virtual 函数是 private 的;有时候 derived 类的重写函数需要调用 base 类的对应虚函数,这时候就没有办法把虚函数设置为 private 的,也就是说这时候就没办法实施NVI方法。【降低了封装性】
class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter {
public:
typdef int (*HealthCalcFunc) (const GameCharacter&);
explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) : healthFunc(hcf)
{}
int healthValue() const
{ return healthFunc(*this); }
...
private:
HealthCalcFunc healthFunc;
};
tr1::function
完成 Strategy 模式:
tr1::function
,这个对象可以保存任何可调用户:class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter {
public:
typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc;
explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) : healthFunc(hcf)
{}
int healthValue() const
{ return healthFunc( *this); }
...
private:
HealthCalcFunc healthFunc;
};
// HealthCalcFunc是个typedef,用来表现 tr1::function的某个实体,行为像一个函数指针。
// 它的含义就是 接受一个指向GameCharacter的引用,并返回int。
// 实现如下:
short calcHealth(const GameCharacter&);
struct HealthCalculator { // 为计算健康设计的成员对象
int operator() (const GameCharacter&) const
{ ... }
};
class GameLevel {
public:
float health(const GameCharacter&) const; // 计算健康的成员函数
...
};
class EvilBadGuy: public GameCharacter {
... // 同前
};
class EveCandyCharacter: public GameCharacter {
... // 另一个人物类型,假设构造函数同EvilBadGuy
};
EvilBadGuy edg1(calcHealth); // 人物1,使用某个函数计算健康指数
EyeCandyCharacter ecc1(HealthCalculator()); // 人物2,使用某个函数对象计算健康指数
GameLevel currentLevel;
...
EvilBadGuy ebg2( std::tr1::bind(&GameLevel::health, currentLevel, _1) // 人物3,使用某个成员函数计算健康指数
{};
class GameCharacter;
class HealthCalcFunc {
public:
...
virtual int clac(const GameCharacter& gc) const
{ ... }
...
};
HealthCalcFunc defaultHealthCalc;
class GameCharacter {
public:
explicit GameCharacter(HealthCalcFunc* phcf = &defaultHealthCalc) : pHealthCalc(phcf)
{}
int healthValue() const
{ return pHealthCalc->calc(*this); }
...
private:
HealthCalcFunc* pHealthCalc;
};
class Shape {
public:
enum ShapeColor { Red, Green, Blue };
// 绘制自己
virtual void draw(ShapeColor color = Red) const = 0;
...
};
class Rectangle : public Shape {
public:
// 重定义 缺省参数值
virtual void draw(ShapeColor color = Green) const;
...
};
class Circle : public Shape {
public:
virtual void draw(ShapeColor color) const; // 注释①
...
};
Shape *ps;
Shape *pc = new Circle;// pc的静态类型是Shape*,动态类型是Circle*
Shape *pr = new Rectangle;// pr的静态类型是Shape*,动态类型是Rectangle*
// 结果如下:
pc->draw(ShapeColor::Red);// 相当于调用Circle::draw(ShapeColor::Red)
pr->draw(ShapeColor::Red);// 相当于调用Rectangle::draw(ShapeColor::Red)
pr->draw();// 也相当于调用Rectangle::draw(ShapeColor::Red)
A
类的 private 中含有 B
类);【has-a 的意思是有一个,也就是内含】D
以 private 形式继承 B
,意思是 D
对象根据 B
对象实现而得,再没有其他含义了】
// 第一种情况:
// 在这种情况下sizeof(HoldsAnInt) = 8
class Empty {};//没有数据,所以其对象应该不使用任何内存
class HoldsAnInt {
private:
int x;
Empty e;// 对齐,没有优化,sizeof(Empty) = 1
};
// private继承的情况:
// 在这种情况下sizeof(HoldsAnInt) = 4
// 这就是所谓的空基类最优化(EBO)
class HoldsAnInt :private Empty {
private:
int x;
};
class File { ... };
class InputFile: virtual public File { ... };
class OutputFile: virtual public File { ... };
class IOFile: public InputFile, public OutputFile
{ ... };