Modern C++ Design

第一章:Policy-Based Class Design

Policy Class (Template)

下面的这些Creater叫做“policy class (template)”。它们类似于设计模式里的strategy。它们是语法导向(syntax oriented),而不是标记导向(signature oriented)。换句话说,这些Creater定义的是”怎样的语法构造符合其所规范的class”,而不是“必须实例化那些函数”。

template <class T>
struct OpNewCreator
{
static T* Create()
{
return new T;
}
};
template <class T>
struct MallocCreator
{
static T* Create()
{
void* buf = std::malloc(sizeof(T));
if (!buf) return 0;
return new(buf) T;
}
};
template <class T>
struct PrototypeCreator
{
PrototypeCreator(T* pObj = 0): pPrototype_(pObj){}
T* Create()
{
return pPrototype_ ? pPrototype_->Clone() : 0;
}
T* GetPrototype() { return pPrototype_; }
void SetPrototype(T* pObj) { pPrototype_ = pObj; }
private:
T* pPrototype_;
};

 

Host Classs Template 

使用policy class (template)的叫做“host class (tempalte)”。host class (template)使用组合或者继承的方式使用policy class (template)。host负责把policy提供的结构和行为组合成更复杂的结构和行为。

template <class CreationPolicy>
class WidgetManager : public CreationPolicy {}

 

使用host和policy:

typedef WidgetManager<OpNewCreator<Widget> > MyWidgetMgr;

template template 参数

从上面的使用方法上看,policy(OpNewCreator)还要接收一个模板类参数(Widget),然而作为一个WidgetManager它只能操作Widget,也就是说万一又一次有个程序员传了另外一个类给policy,那么就会因为和WidgetManager不兼容而出现问题。为了避免这种风险,可以使用“template template 参数”。

template <template <class Created> class CreationPolicy>
class WidgetManager : public CreationPolicy<Widget>
{...}

关于template tempalte parameter, 可以这么理解:WidgetManager这个类,是一个带有模板参数的类,它的模板参数是template <class Created> class CreationPolicy>,这个模板参数本身又是一个模板。使用作为模板参数的这个模板给WidgetManager指定基类:CreationPolicy<Widget>。可以印证WidgetManager的模板参数声明和使用的都是一个模板。这个作为WidgetManager的模板参数的模板的参数声明为class Created,使用为CreationPolicy中的Widget。

使用这个用模板做模板参数的模板的代码如下:

typedef WidgetManager<OpNewCreator> MyWidgetMgr;

用模板作为模板参数还有一个好处,那就是:因为现在WidgetManager的模板参数是一个模板,你可以用这个模板来实例化别的模板类。比如在WidgetManager中已经使用了CreationPolicy<Widegt>(作为基类),你也可以把CreationPolicy派其它用途,比如我可以在WidgetManager里面使用CreationPolicy<Gadget>生成一个Gadget。

实际使用中,还可以给模板参数默认值,比如:

template <template <class Created> class CreationPolicy = OpNewCreator>
class WidgetManager : public CreationPolicy<Widget>{...}

析构函数:

在上面的例子中,由于host类继承了policy类,所以用户可能会把host类指针向上cast到policy指针,并且删除它,这个时候为了保证子类(host)的析构函数会被调用,基类(policy)的析构函数必须是虚函数。然而引进虚函数会造成一定开销,所以我们想避免虚函数!怎么避免呢?方法如下:

给基类(policy)定义一个非虚的,但是处于protected级别的析构函数,在子类(host)的析构函数适当的调用基类(policy)的析构函数。这样,由于基类的析构函数是protected,那么只有host才能去调用之,也就是说如果你删除一个policy的指针,那是非法的。这样通过强行要求用户操作子类(host),避免了基类(policy)析构函数非虚的问题。

通过不完全具现化而获得的选择性机能

这话听着有点绕,但是它所实现的功能很美妙:)

问题是这样的:host 模板 WidgetManager所采用的policy可能是很简单的OpNewCreator,也可能是比较复杂的PrototypeCreator。注意所采用的policy决定了WidgetManager的基类。OpNewOperator只有一个Create函数给WidgetManager使用,而PrototypeCreator还有一个SetPrototype函数,而这个函数也必须要暴露给应用者。

问题就在于在这个暴露的方式:WidgetManager的所有逻辑都应该适用于所有的policy,那么似乎把PrototypeCreator所独有的函数暴露给WidgetManager不合适。

然而模板有一个特性:如果class template有一个成员函数没有被用到,那么编译器就不会去理会它,甚至不会为它进行语义检查。因此,我们可以这样暴露SetPrototype函数于WidgetManager中:

template <template <class Created> class CreationPolicy>
class WidgetManager : public CreationManager<Widget>
{
...
void SwitchPrototype(Widget* pNewPrototype)
{
CreationPolicy<Widget>& myPolicy = *this;
delete myPolicy.GetPrototype();
myPolicy.SetPrototype(pNewPrototype);
}
}

看上面的SwitchPrototype函数,如果你使用了PrototypeCreator作为policy,那么你可以使用SwitchPrototype;如果你使用了其它policy,你使用SwitchPrototype,会编译出错;如果是使用了其它prototype,但是没有使用SwitchPrototype,那么编译也会通过!这里面的关键点就在于我们得益于template的“不完全具现化而获得的选择性机能”(如本节的标题)

TODO

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

原文链接: https://www.cnblogs.com/dbbs/archive/2011/12/28/2304491.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    Modern C++ Design

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/39297

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月8日 下午3:58
下一篇 2023年2月8日 下午3:58

相关推荐