条款34 区分接口继承和实现继承
条款35 考虑virtual函数以外的选择
区分接口继承和实现继承
在设计类成员函数时,一般既不要将所有函数都声明为non-virtual(普通函数),这会使得没有余裕空间进行特化工作;也一般不要将所有函数都声明为virtual(虚函数或纯虚函数),因为一般会有一些成员函数是基类就可以决定下来的,而被所有派生类所共用的。这个设计法则并不绝对,要视实际情况来定。
总结
- 接口继承和实现继承不同。在public继承之下,derived class总是继承base class的接口;
- pure virtual函数只具体指定接口继承;
- impure virtual函数具体指定接口继承和缺省实现继承;
- non-virutal函数具体指定接口继承以及强制性实现继承。
考虑virtual函数以外的选择
使用non-virtual interface的方式实现Template Method模式
直接上代码:
class Base
{
public:
virtual void work() const //work
{
cout <<"Base::work"<<endl;
dowork();
}
private:
//具体的work 子类需要实现自己的dowork() 以便 work()函数调用
//子类不实现默认调用 Base的dowork()
virtual int dowork() const
{
cout <<"Base do work"<<endl;
}
};
class Derived: public Base
{
public:
private:
virtual int dowork() const
{
cout <<"Derived do work "<<endl;
}
};
int main(int argc, char *argv[])
{
Derived d;
d.work();
return 0;
}
//打印结果
Base::work
Derived do work
显然不重写dowork函数是调用父类的函数。这种方法和“钩子方法“使用的原理是差不多的。
这个函数也被称为virtual函数的外覆器(wrapper)。
使用function pointers的方式实现Strategy模式
例如在设计一个游戏角色的健康成长设置的时候。我们可以直接指向一个健康计算函数,可直接调用函数进行计算
class GameCharacter;
//缺省的健康计算 (正常人) 还可能有体力好的人 恢复健康就很快等等
int defaultHealthCalc(GameCharacter&);
class GameCharacter
{
public:
typedef int (*healthCalcFunc)(GameCharacter& gc);
explicit GameCharacter(healthCalcFunc hcf = defaultHealthCalc) : m_hcf(hcf){}
//获取一个角色的健康值
int healthValue() const
{
return m_hcf(*this);
}
private:
healthCalcFunc m_hcf;
};
这样使用函数指针的方式,弹性就更大了。
我们的子类可以继承这个类:
class Guy : public GameCharacter
{
public:
explicit Guy(healthCalcFunc hcf = defaultHealthCalc):GameCharacter(hcf){}
...
};
//提供不同体质的人的计算方法
int loseHealthQuickly(const GameCharacter&);
int loseHealthSlowly(const GameCharacter&);
//可以创建不同体质的人
Guy g1(loseHealthQuickly);
Guy g2(loseHealthSlowly);
当然可以提供函数设置这个函数指针。
但是这样还是有问题,当我们需要使用non-member函数访问class的non-public成份的唯一方法就是弱化封装。
使用tr1::function的方式实现Strategy模式
使用函数指针的约束很明显,必须是函数,固定的返回值与参数。但是tr1::function则没有这些现实,这样的对象可持有任何可调用物(函数指针、函数对象、成员函数指针),只要声明格式兼容与需求。
只需要将上面类的函数指针声明替换成:
typedef std::tr1:;function<int(const GameCharacter&)> HealthCalcFunc;
举个简单例子:
void func(string str)//带调用的函数
{
qDebug() <<"func:"<<str;
}
struct Func//函数对象
{
void operator ()(string str)
{
qDebug() <<"Func:operator:"<<str;
}
};
std::function<void(string str)> pfunc;//声明
int main(int argc, char *argv[])
{
pfunc = func;
pfunc("hello");
Func f;
pfunc = f;
pfunc("world");
return 0;
}
//打印结果
func:hello
Func:operator:world
常见的Strategy模式
这个想必都很熟悉了,实现比较简单,但是呢始终不如tr1::funtion和tr1::bind酷。。。