alex

alex

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  7 随笔 :: 6 文章 :: 7 评论 :: 0 Trackbacks

2007年2月11日 #

今天来讲讲怎么编写异常安全的代码。
程序运行过程中,往往需要对一些流程加入异常处理,来提高程序的robust.比如
通过try catch来捕捉异常
try
{
    pMemory = new char[MAX_BUF_SIZE];
}
catch(std::bad_alloc& e)
{
    //error handling,er:do something resource free
}
但在程序代码段中出现大量的try catch,不仅从美观,效率和程序输写上都是不怎么好。
而另外一种对于异常的处理方法是依赖于c++的ctor/dctor的匹配来做的,就是所谓的
RAII,这个很容易让人联想到std::auto_ptr
std::auto_ptr<int> tmp(new int);
通过new分配的对象会在tmp生命结束后,释放相关的资源,通过这种方式,就能保证在程序异常,或退出时,已分配的对象能正确自动的释放拥有的资源,而在对象声明周期内,可以保证资源的有效性。
这种方式就是今天blog要写的主要内容,我们可以看到std::auto_ptr作用范围很小,只能对从堆上分配的对象进行管理,假如对文件打开句柄实行RAII,你也许会认为再写个不就是了,但这样只会造成源代码里充满了这些资源管理的类,这导致了一个严重的问题,好的结构在繁琐的流畅前面变的难堪。
那怎么样对这个进行泛化,从而能对比如从简单的指针释放,文件句柄维护,甚至相关的成员函数。我们来看下loki::socpeguard是怎么实现的:
先看下基本的用法
(1)  FILE* hFileOpen = fopen(....);
(2)  LOKI_ON_BLOCK_EXIT(flose,hFileOpen);
line2会在出LOKI_ON_BLOCK_EXIT域或程序异常结束时被调用,下面是对类成员的调用
void CTestObject::Create
{
    LOKI_ON_BLOCK_EXIT_OBJ(*this,FressResouce);
    ...
}
同上面差不多,会在这个函数结束后或异常结束后调用类成员的FreeResource.在正常流程结束后,可以通过调用Dismiss来防止对FreeResouce的调用,即类似数据库操作的commit操作。下面来分析下LOKI的实现:
从上面可以看到,RAII的是:
1:构造函数对资源的获取
2:稀构函数对资源的释放
先来看下LoKi对上面那2个宏的定义
#define LOKI_ON_BLOCK_EXIT      Loki::ScopeGuard LOKI_ANONYMOUS_VARIABLE(scopeGuard) = Loki::MakeGuard

#define LOKI_ON_BLOCK_EXIT_OBJ  Loki::ScopeGuard LOKI_ANONYMOUS_VARIABLE(scopeGuard) = Loki::MakeObjGuard
上面的Loki::ScopeGuard是一个基类的别名
typedef const ScopeGuardImplBase& ScopeGuard;
而LOKI_ANONYMOUS_VARIABLE(scopeGuard)用来我们产生唯一的名字,有时假如需要调用Dismiss的话,则需要自己去实现宏定义的内容,这样才能通过对象访问。Loki::MakeGuard或Loki::MakeObjGuard是用来产生对象的实际类型的,下面是一个
Loki::MakeGuard的例子:
template <typename F, typename P1>
inline ScopeGuardImpl1<F, P1> MakeGuard(F fun, P1 p1)
{
    return ScopeGuardImpl1<F, P1>::MakeGuard(fun, p1);
}
可以看到ScopeGuardImpl1<F, P1>是要产生的具体类型,MakeGuard通过函数参数的数目来重载的,而MakeGuard此处的作用是要睡死了...-_-'',作用是利用函数自动推导出参数的类型,这样就免去了指定ScopeGuardImpl1的类型的麻烦,而
ScopeGuardImpl1<F, P1>::MakeGuard(fun, p1);
简单的返回对象的一个临时变量,并assign给一个上面的一个scopeguard的实例,这里依赖一个C++的特性,临时变量的声命周期和通过他初始化的引用类型的声明周期是一致的。

从上面可以看到Loki定义了一个ScopeGuardImplBase的基础类。这个类定义了一个基本的方法Dismiss,以及相关的状态。下面是loki中这个类的定义
class ScopeGuardImplBase
{
    ScopeGuardImplBase& operator =(const ScopeGuardImplBase&);
 protected:
    ~ScopeGuardImplBase()
    {}
    ScopeGuardImplBase(const ScopeGuardImplBase& other) throw()
        : dismissed_(other.dismissed_)
    {
        other.Dismiss();
    }
    template <typename J>
    static void SafeExecute(J& j) throw()
    {
        if (!j.dismissed_)
           try
           {
              j.Execute();
           }
           catch(...)
           {}
     }
       
     mutable bool dismissed_;
public:
     ScopeGuardImplBase() throw() : dismissed_(false)
     {}
     void Dismiss() const throw()
     {
         dismissed_ = true;
      }
};
可以看到类里面定义了上面所说的一些属性,其中SafeExecute用来提供子类同一的资源释放方法,并调用子类的方法来具体操作,因为相关的函数,变量都保存在具体的子类,可以看到这个函数使用了try catch,这里加这个的目的是,因为资源释放要在子类的稀构里被触发,而调用具体的方法是外面传进来的,所以无法保证一定是异常安全的,而假如在稀构里面异常的话,会导致程序的行为无法定义。
下面具体来看下一个子类的实现:
template <typename F, typename P1>
class ScopeGuardImpl1 : public ScopeGuardImplBase
{
public:
    static ScopeGuardImpl1<F, P1> MakeGuard(F fun, P1 p1)
    {
        return ScopeGuardImpl1<F, P1>(fun, p1);
    }
    ~ScopeGuardImpl1() throw()
    {
        SafeExecute(*this);
    }
    void Execute()
    {
        fun_(p1_);
    }
protected:
    ScopeGuardImpl1(F fun, P1 p1) : fun_(fun), p1_(p1)
    {}
    F fun_;
    const P1 p1_;
};
在LoKi里面可以看到很多类似ScopeGuardImpl1的定义,比如ScopeGuardImpl0,
ScopeGuardImpl2,可以发现最后面的数字表示具体参数的数目。
可以看到上面所说的MakeGuard的定义,以及对基类方法的调用,可以看到构造函数接收的类型,一个函数对象,和一些参数对象,并保存,对于成员函数的scopeguard,LoKi定义了1些相似的类,主要是增加了对象的引用,还有就是函数的调用方式上。
上面可以看到参数是通过值的方式来保存的而不是通过引用。而且是const属性的,下面是相关的分析。
1:通过传值的方式,从而避免了异常抛出时,可能引用的对象被稀构
2:加const属性,从而保证了在func需要参数是reference时而保存的参数确是非const时产生相应的编译错误,因为对reference传人const non-reference形式是错误的。
而对于1的方式,存在的一种问题是假如操作的fun需要传入引用,那传进去的值就无法在释放的函数中被改变,而2是对这种的一种类似契约似的编程,Loki 提供的方法是通过一个中间对象来保存操作参数的引用,并赋予这个对象自动转换功能。下面是这个类的定义:
template <class T>
class RefToValue
{  
public:
    RefToValue(T& ref) : ref_(ref)
    {}
    RefToValue(const RefToValue& rhs) : ref_(rhs.ref_)
    {}
    operator T& () const
    {
        return ref_;
    }
private:
    // Disable - not implemented
    RefToValue();
    RefToValue& operator=(const RefToValue&);
       
    T& ref_;
};
可以很清楚的看到类的实现,下面是一个工具类
template <class T>
inline RefToValue<T> ByRef(T& t)
{
     return RefToValue<T>(t);
}
下面给个具体的例子,假如
template<typename _Ty>
void SAFEDELETE(_Ty*& ptr)
{
   if (NULL != ptr)
      delete ptr;
   ptr = NULL;
}

char* ptr = new char;
 
{
    LOKI_ON_BLOCK_EXIT(SAFEDELETE<char>,Loki::ByRef(ptr));
}
 
if (NULL == ptr)
  std::cout << "NULL" << std::endl;
基本上就这么多了,sleep去了
                                                    alex_yuu

posted @ 2007-02-11 15:55 agerlis 阅读(1355) | 评论 (3)编辑 收藏

前几天一个朋友给我看了1段代码:
any temp; //any is a class
temp = 1;
temp = "a";
temp = x; //x is a abstract class

看到这段代码着实下了1跳.初期的感觉象void*指针那样,又象variant变量。但感觉还是比较新颖的,-_-''也许我是菜鸟的原因,在脑袋转了一下后,我实现了自己的一个类,用来接受任何参数,起初我认为any应该是个typedef,而接收的类型也是前面知道的(-_-''又没弄清需求就去实现了),所以实现一个接收任何参数的类需要个typelist,和对每种类型的泛化,下面是class的定义:

template<typename _typelist>
class any
{
public:
 typedef typename Loki::TL::TypeAtNonStrict<_typelist,0>::Result param1;
 typedef typename Loki::TL::TypeAtNonStrict<_typelist,1>::Result param2;
 typedef typename Loki::TL::TypeAtNonStrict<_typelist,2>::Result param3;
 typedef typename Loki::TL::TypeAtNonStrict<_typelist,3>::Result param4;
 typedef typename Loki::TL::TypeAtNonStrict<_typelist,4>::Result param5;

 any(param1 param):
  m_param1(param){}

 any(param2 param):
  m_param2(param){}

 any(param3 param):
  m_param3(param){}

 any(param4 param):
  m_param4(param){}

 template<typename _Ty>
 const _Ty&  Get()
 {
     class CERROR_ACCESS_DENIED;
     LOKI_STATIC_CHECK((Loki::TL::IndexOf<_typelist,_Ty>::value != -     1),CERROR_ACCESS_DENIED);

     return __Access<_Ty>();
 }
private:
 template<typename _Ty>
 _Ty&  __Access();

 template<>
 param1& __Access<param1>() {return m_param1;}

 template<>
 param2& __Access<param2>() {return m_param2;}

 template<>
 param3& __Access<param3>() {return m_param3;}

 template<>
 param4& __Access<param4>() {return m_param4;}

 param1  m_param1;
 param2  m_param2;
 param3  m_param3;
 param4  m_param4;
};

typedef any<Loki::TYPE_LIST_3(int,float,char)> ANY;

这样似乎就可以接收任何类型了,也可以取出数据,取出时必须要知道相应的类型,而且,假如类型不在列表里面,则编译期出错
class CERROR_ACCESS_DENIED;
LOKI_STATIC_CHECK((Loki::TL::IndexOf<_typelist,_Ty>::value != -     1),CERROR_ACCESS_DENIED);
这样,-_-''似乎就可以了,只要所接收的类型可以拷贝即可。但有个明了的缺陷,那1就是接收的类型必须编译期写死,假如不写死的话,我个人开始的认为是加一个基类,-_-''
还是要3q我那位朋友,让我了解到boost::any的用法
下面是boost::any的具体分析,我们要达到下面几个要求:
1:可以接收任何类型的数据(具有value属性)
2: 可以方便的取出数据
3:型别安全,不象void*
下面是boost::any的实现,我按部分贴,而且省去我认为不重要的东西,因为太长了,这会影响我blog的收视率。
1:首先来看下他的构造和稀构(-_-''错别字)
 any()
    : content(0)
{
}
 
 template<typename ValueType>
 any(const ValueType & value)
    : content(new holder<ValueType>(value))
{
}
 
any(const any & other)
    : content(other.content ? other.content->clone() : 0)
 {
 }
 
 ~any()
 {
     delete content;
 }
从构造函数可以看到,any可以接受默认的,任何其他值包括ant本身,在稀构里面可以看到对content的释放,content在这里是一个基类的指针,是any内部实现的,我们可以看到,在对any构造是有种方法:
(1)content(new holder<ValueType>(value))
(2)content(other.content ? other.content->clone() : 0)
稍后会看到holder是派生自content类型的一个模板实现,也许讲到这里,有些应该明白了any是怎么保存任何类型的吧,对于其他any的构造,我们发现调用了content的一个clone方法,很明显这是content的一个虚方法,这个方法的存在得以让我们运用虚函数的机制正确的拷贝对象,下面会看到这只是个简单的new操作。
2:来看下any的operator =的重载
any & swap(any & rhs)
{
     std::swap(content, rhs.content);
     return *this;
}
 
template<typename ValueType>
any & operator=(const ValueType & rhs)
{
    any(rhs).swap(*this);
    return *this;
}
 
any & operator=(const any & rhs)
{
      any(rhs).swap(*this);
      return *this;
}
可以看到operator =,接收任何其他类型的参数,swap方法的实现,用来交换2个对象的content指针,我们可以看到operator =里面临时对象的构建,在函数结束后,自动释放原来content的对象,这有点RAII味道。(http://blog.sina.com.cn/u/1019039487)独家小菜。。
3:下面来看下辅助类
 bool empty() const
 {
      return !content;
 }
 
 const std::type_info & type() const
 {
      return content ? content->type() : typeid(void);
 }
可以看到。一个empty,一个type,很形象。
4:下面来看下上面content所指的对象,以及any怎么保存任何类型,以及这种怎么保证型别安全
class placeholder
{
public: // structors
     virtual ~placeholder()
     {
     }
public: // queries
      virtual const std::type_info & type() const = 0;
      virtual placeholder * clone() const = 0;
};
 
template<typename ValueType>
class holder : public placeholder
{
public: // structors
     holder(const ValueType & value)
              : held(value)
     {
     }
public: // queries
     virtual const std::type_info & type() const
     {
         return typeid(ValueType);
     }
 
     virtual placeholder * clone() const
     {
          return new holder(held);
     }
public: // representation
     ValueType held;
};(-_-''格式矫正真是类啊)
 
placeholder就是上面content指向的对象,可以看到他定义的一些提供给any调用的函数和虚基本必须的虚稀构函数(可以看到any对content调用了delete)
下面是holder的实现,holder是一个模板,里面定义的
ValueType held;
很明星是any用来保存任何类型的,回顾下上面所说的any初始化content的2种方法
(1)content(new holder<ValueType>(value))
(2)content(other.content ? other.content->clone() : 0)
从这个地方可以看到怎么初始化holder对象,并保存到content.注意到这里的held是public的,这样就提供了对数据访问的功能,在访问的时候必须要知道具体要访问的类型,才能调用相应的static_cast或者dynamic_cast来操作,而这样其实也提供了型别安全的保证,eg:不象malloc,返回void*,然后()转换一下。
5:看下怎么访问any里面的属性值:这里就不列举出所有的实现,因为有些是对const的版本。
template<typename ValueType>
ValueType * any_cast(any * operand)
{
    return operand && operand->type() == typeid(ValueType)
                    ? &static_cast<any::holder<ValueType> *>  (operand->content)->held
                    : 0;
}
 
template<typename ValueType>
ValueType any_cast(const any & operand)
{
     typedef BOOST_DEDUCED_TYPENAME        remove_reference<ValueType>::type nonref;
 
   const nonref * result = any_cast<nonref>(&operand);
   if(!result)
            boost::throw_exception(bad_any_cast());
    return *result;
}//这里去掉1些原来实现的代码和注释
可以看到具体的实现方式&dynamic_cast的逻辑差不多,对引用的cast有可能抛出异常。通过上面这种方式,就达到了对数据的读取,读取时必须知道里面的数据类型。可以看出any只能保存一个值,-_-''不向我那个,可以同时保存不同类型的值。
6:extension
可以通过std::vector<boost::any> anys,来构建一个任何类型的列表,但访问比较麻烦。
也可以通过any来达到虚函数不能为template的限制(因为一个是静态的一个是动态的)
象下面:
class CBase
{
 public:
     virtual void Set(boost::any) = 0;
};
 
class CHild:
     public CBase
{
 public:
     virtual void Set(boost::any param)
     {
          m_value = param;
     }
 private:
     boost::any  m_value;
};
 
CHild test;
CBase* ptr = &test;
ptr->Set(1);
ptr->Set('a');
7.finish...总算完成了...-_-''要变唐僧了...
                                                  alex. agerlis@163.com
posted @ 2007-02-11 15:54 agerlis 阅读(411) | 评论 (0)编辑 收藏

听说过一种时间管理的方法,是用来怎么安排任务和时间的,下面是大概的概念图:
 

                               time - task

图中箭头指向的方向表示紧迫度的添加,那么下面是关于1,2,3,4的定义:

一:任务比较重要,但相应的时间也很紧

二:任务是长期任务,但比较重要

三:不那么重要的任务,时间也很多

四:不需要多长时间的非重要任务

现在分析下上面的这些:
对于一:往往这个时候处于时间的dead line状态,而此时往往是我们都不想要的,熟话说事情是急不来的,这个时候往往会导致质量的下降.

对于二:我们有充足的时间来做重要的任务,这正是我们想要的,但,怎么做才能使其不会过渡到区间一呢,转为区间1往往会由于人的惰性产生的,关键是要提前做事.安排好时间.

对于三:往往是一些可以辅助我们成长的事情,这些东西对我们有益处,这对全面的发展起到了很好的作用,-_-''就象我学吉他..

对于四:是些时间紧,但不重要的任务,比如要帮助人去google一些资料.

从上面可以看到,区间一实际上是最差的,但为了做好区间2,我们必须要尽量减少区间4的份量,而对于全面的发展,我们也要安排时间到区间3.这也是很重要的.

posted @ 2007-02-11 15:54 agerlis 阅读(340) | 评论 (0)编辑 收藏

以前C读取命令行参数感觉太麻烦,而且不容易记忆.下面是另外一种命令行的读取方法
通过GetCommandLine(),来返回当前的命令行:
setlocale(LC_ALL, ".ACP");
std::wcout << GetCommandLine() << std::endl;
下面是输出:
"e:\学习程序\consol3\debug\consol3.exe" a b c
对于命令行的参数读取用CommandLineToArgvW,可以把命令行参数转换成字符串数组的形式
int nums = 0;
TCHAR** temp = CommandLineToArgvW(GetCommandLine(),&nums);
for(int i = 0; i < nums; i++)
{
  std::wcout<< temp[i] << std::endl;
}
下面是输出:
e:\学习程序\consol3\debug\consol3.exe
a
b
c

可以看到命令行参数从下标=1的元素开始.
感觉这种形式比较好记忆
posted @ 2007-02-11 15:53 agerlis 阅读(981) | 评论 (0)编辑 收藏

abstract factory.感觉是个很玄的名字,即使在我写学习笔记的时候,我也因为缺少现实中的实践而对其了解不深。^^希望.... 多指正
Name:
   abstract factory
Addressed Problem:
   提供对同1种相关对象的创建。比如在程序里面,希望根据用户配置来设置UI方案,比如方案1和2,对应的UI也存在2套不同的实现类,比如对于ScrollBar,有CScrollBar1和CScrollBar2,都继承自ScrollBar。也许在程序里面有很多这样的类。这样你在应用UI实例化类时就必须要选择比如
IScrollBar* pScrollBar = NULL;
if (_use_ui_1)
   pScrollBar = new CScrollBar1;
else
   pScrollBar = new CScrollBar2;
也许你这样感觉没多大关系,但想象下,在程序的多处都存在这样的选择,为什么我不选择一种less error phone的方法呢?比如
pScrollBar = Create(...);
这样一种简单的方式呢?而且上面的写法也引进了1个避短,也就是在产生pScrollBar时,涉及到了具体的实现类,这在面向对象这种program to interface这样的语言中是很忌讳的事情。比如,在加种UI方案,也许你会再写个else,这样等于自己把自己往火堆里面推。你是一个勤奋的程序员,但不是个优秀的程序员^^.也许有人会说,我可以利用prototype这样的方式来啊。这样,在程序的外面配置一下每个类的prototype就可以了,或许也可以用object factory啊,都可以(^^似乎是哦,我也没学过这2个的说)解决上面的问题,一个通过DoClone类似的,1个通过ID或其他的,其实在应用上面的2种时,自然的导出了abstract factory.为什么呢,因为上面的2种,你都需要设置多个的原型或ID。比如也许在CUIManager的构造里面写贼大的if
if (_use_ui_1)
{
    ...//设置UI_1下所有的prototype
}
else
{
    ...//设置UI_2下所有的prototype
}
也许在将来的莫天,在加上UI_3,则。再写个if.也许大家可能会想,我在应用ui时,我就知道了所要创建的对象,为什么我不把上面宝装一下改成:
if (_use_ui_1)
 //设置factory_1
else
 //设置factory_2
然后在抽象出来的IFactory里面提供创建这些UI Class的方法,比如,CreateScrollBase.等等,这样就有了abstract factory 的雏形。
基础实现
/*
 测试abstract factory模式
*/

class IScrollWindow
{
public:
 virtual void DrawScroll(void) = 0;
};

class IListWindow
{
public:
 virtual void DrawList(void) = 0;
};

class ICreateWindowFactory
{
public:
 virtual IScrollWindow* CreateScrollWindow(void) = 0;

 virtual IListWindow* CreateListWindow(void) = 0;
};

class CBlueWindowFactory:
 public ICreateWindowFactory
{
 class CBlueScrollWindow:
  public IScrollWindow
 {
 public:
  virtual void DrawScroll(void)
  {
   std::cout << "draw blue scroll" << std::endl;
  }
 };

 class CBlueListWindow:
  public IListWindow
 {
 public:
  virtual void DrawList(void)
  {
   std::cout << "draw blue list" << std::endl;
  }
 };
public:
 virtual IScrollWindow* CreateScrollWindow(void)
 {
  return new CBlueScrollWindow;
 }

 virtual IListWindow* CreateListWindow(void)
 {
  return new CBlueListWindow;
 }
};

class CRedWindowFactory:
 public ICreateWindowFactory
{
 class CRedScrollWindow:
  public IScrollWindow
 {
 public:
  virtual void DrawScroll(void)
  {
   std::cout << "draw red scroll" << std::endl;
  }
 };

 class CRedListWindow:
  public IListWindow
 {
 public:
  virtual void DrawList(void)
  {
   std::cout << "draw red list" << std::endl;
  }
 };
public:
 virtual IScrollWindow* CreateScrollWindow(void)
 {
  return new CRedScrollWindow;
 }

 virtual IListWindow* CreateListWindow(void)
 {
  return new CRedListWindow;
 }
};
在需要用的地方,就可以这样:
ICreateWindowFactory* pCreateWindow1 = new CBlueWindowFactory;

 pCreateWindow1->CreateListWindow()->DrawList();
 pCreateWindow1->CreateScrollWindow()->DrawScroll();
假如需要用不同的工厂,则更换不会影响到调用处的代码。因为掉用工厂的地方是面向接口的。其实abstract factory的理念应该是比较简单的(^^瞎猜的).基本讲完了什么是抽象类工厂,他要解决的一些问题以及怎么解决和1个小而乱的demo代码段。下面来看下我们怎么泛化这个类工厂,这个会涉及到loki里面的具体实现,大家要加满油啊,因为泛化类工厂是一件不容易的事情啊。

泛化_1
首先,(^^这部分我也不是很懂)要泛化的是abstract factory的接口,就象上面的CreateScrollWindow和CreateListWindow,在泛化时需要的信息是要创建的同1组对象的相关接口比如IScrollBar,IList等等,在loki里面,要为1个类泛化1组接口,可以通过GenScatterHierarchy来将unit应用到typelist里的每1个类型,并将该类从unit<type>派生,从而得到1组接口。GenScatterHierarchy做了什么呢,他产生了啥呢?具体的可以看morden c++里面的实现。通过GenScatterHierarchy我们得到了我们要得1组接口。下面是loki里面对这个得相关实现
template <class T>
class AbstractFactoryUnit
{
public:
    virtual T* DoCreate(Type2Type<T>) = 0;
    virtual ~AbstractFactoryUnit() {}
};

可以看到,上面定义了2个函数,而这个类就是我上面说得调用GenScatterHierarchy时,具现化时对typelist得每个类型应用得template类,而最后产生得也将是类似AbstractFactoryUnit<IScrollBar>的类,我们具体的抽象工厂从这些派生。至于pure dctor这个大家应该都知道啥作用。下面来看Abstract Factory 的泛化:

template
<
    class TList,
    template <class> class Unit = AbstractFactoryUnit
>
class AbstractFactory : public GenScatterHierarchy<TList, Unit>
{
public:
     typedef TList ProductList;
       
     template <class T> T* Create()
     {
        Unit<T>& unit = *this;
        return unit.DoCreate(Type2Type<T>());
     }
 };
可以看到这个即由GenScatterHierarchy来得到了我们想要的东西。提供了Create的模板函数,使得我们可以象这样factory.Create<IScrollBar>()的方便形势来调用。ProductList是对于抽象工厂要创建的类型的重命名。方便后面在产生实际的类型时,获取对应的类型信息,对于DoCreate的参数,大家应该都明白这是重载用的,那用在哪里呢?下面会介绍。

泛化_2
在辛苦介绍完泛化抽象工厂的接口后,我们可以通过类似的方式来定义1个abstract factory的接口
Loki::AbstractFactory<LOKI_TYPELIST_2(IScrollBar,IListWindow)>
下面我们来介绍最后的,我们怎么来提供抽象工厂的实现,首先是对象的创建,loki里面提供了默认的创建的方法,当然我们可以修改或用特化的版本来做选择。
template <class ConcreteProduct, class Base>
class OpNewFactoryUnit : public Base
{
     typedef typename Base::ProductList BaseProductList;
   
protected:
     typedef typename BaseProductList::Tail ProductList;
   
public:
     typedef typename BaseProductList::Head AbstractProduct;
     ConcreteProduct* DoCreate(Type2Type<AbstractProduct>)
     {
          return new ConcreteProduct;
     }
};
可以看到DoCreate是我们的核心部分,里面调用了new来创建对象。而这里也让我们看到这应该是上面创建对象的重载。而Type2Type的作用正是在这里体现作用,因为c++无法通过函数返回值来重载不同的对象。也许你会看到上面的一些类型定义,包括OpNewFactoryUnit的2个模板参数,第2个模板参数是实现GenLinearHierarchy必备的GenLinearHierarchy和上面的GenScatterHierarchy的核心思想一样,都是通过具现化来实现的,不过GenLinearHierarchy产生的是线性的继承体系,中间夹杂着比如OpNewFactoryUnit<IScrollBar,GenLinearHierarchy<...> >这样的形势。下面来看下抽象工程的具体实现的泛化,联系起来就能对上面的理解了
template
<
    class AbstractFact,
    template <class, class> class Creator = OpNewFactoryUnit,
    class TList = typename AbstractFact::ProductList
>
class ConcreteFactory
     : public GenLinearHierarchy<
     typename TL::Reverse<TList>::Result, Creator, AbstractFact>
{
public:
    typedef typename AbstractFact::ProductList ProductList;
    typedef TList ConcreteProductList;
};
可以看到ConcreteFactory由GenLinearHierarchy来驱动产生我们想要的,本来这个贴图比较明朗点,但我懒的画,哈哈。从GenLinearHierarchy的参数来看,第1个是具体的实现类的typelist,比如LOKI_TYPELIST2(CScrollBar_1,CListWindow_1),至于为什么要对类型做reverse操作,因为在类的基础体系产生后,typelist的第1个元素,在继承体系是由下往上的,而于上面由OpNewFactoryUnit等定义的ProductList的Head定义的自上往下的是相反的,所以这里应用了reverse操作。Creator就是上面的OpNewFactoryUnit或你自定义的元素。并在具现化是,应用typelist的每个类型。AbstractFact是继承体现的最顶端,这个应该很明显,就是上面定义的AbstractFactory,这样啥都明确了,具体的接口,对接口的函数的重载都已经泛化完成。下面是(^^睡死了,睡觉去了,简单从便,上面均未调试哦).至于为什么会把第3个参数默认为抽象类工厂接口的ProductList,这个和loki用基于prototype的Creator有关系。。(^^俺是菜鸟,就只能到这里了)
对abstract factory的看法,优点在上面的已经说过了,确定在,我们要添加1种对象的创建时,都要去修改接口的定义,当然后面的泛化也为我们解决了些问题,但泛化对于参数的传递不怎么好用,可以通过提供新的OpNewFactoryUnit来适当解决。
                                                    agerlis.2007.2.10 0:22
posted @ 2007-02-11 15:53 agerlis 阅读(372) | 评论 (0)编辑 收藏

handle-body,睡死了,但还是得坚持下。其实这个是很简单的概念
这个也称呼为pimpl,是private implemention的缩写,下面涉及到的都叫做pimpl, 那用这个到底有什么好处呢:
1:可以把具体的实现从client端封装,这样即使修改实现,只要不修改头文件,对client端来说,是没有任何影响的,甚至不改变代码的2进制特性
2:加速编译时间,我们知道有时头文件有太多的实现,会导致包含这个头文件的其他文件的编译时间增加,通过pimpl,我们把实现从头文件移到cpp里面,从而减少了其他包含这个文件的编译时间,加快编译速度
3:减少依赖,这个实际上也增加了编译的速度,可以不对client端暴露一些实现上的头文件,从而减少了依赖说了这么多,下面来看下一个简单的pimpl的实现。是用loki的类来实现的。哈哈,睡死了,偷懒
/*.h*/
class CTest
{
public:
    void Test();
private: 
    Loki::PimplT::Type m_impl;
};
------------------------------------------------------------------- /*.cpp*/
template<>
struct Loki::ImplT
{
public:
    void Test() { std::cout << "test" << std::endl; }
};

void CTest::Test() { return m_impl->Test(); }

上面只是个很简单很简单的例子,以至于有些还无法体会到pimpl的作用,但相信在实践中你会体会到,把原来定义在CTest里面的一些私有的方法,成员,全部挪到ImplT里面时,你会体会到,这个CTest的头文件里include了很少的头,他可以说完成了原始的使命,达到了功能性接口的作用,而其他原来定义在头里的.h,可以全部挪到cpp里了。下面来分析下loki是怎么实现的。可以看到实现pimpl首先需要牵置声明pre define类,loki通过模板的全特化来实现了这个的隐藏,从而避免了把具体的实现类暴露给client的弊端。下面来看下这个泛化的pre define: template struct ImplT; 哈哈,很简单,再看上面我的cpp里的定义,大家结合上面也许就明白了吧,部过,这里需要注意的是,CTest的构造函数要写后面,不然can;t compile. 但是,为什么具体的实现类是ImplT但,前面的
Loki::PimplT::Type m_impl;
那Loki::PimplT::Type是什么用的,我们知道,假如保存原始的
Loki::ImplT<CTest>* m_impl;
然后new一个,其实这样只存在1个问题,2个不方便,
1:问题是这样的原始指针,不能传播const信息,也就是说,对象CTest转为const的化,他里面的指针类型不是同样随着带有const属性,也就是说,假如此时指针指向的对象有个函数,提供了const&none const的,此时,还是会调用non const的,所以,外界还是可以通过这个对象来做修改。
2:2个不方便是,你必须对对象实现new和delete.
下面来看下loki是怎么做的
template<class T>
struct ConstPropPtr
{
        explicit ConstPropPtr(T* p) : ptr_(p) {}
        ~ConstPropPtr() { delete  ptr_; ptr_ = 0; }
        T* operator->()    { return  ptr_; }
        T& operator*()    { return *ptr_; }
        const T* operator->() const    { return  ptr_; }
        const T& operator*()  const    { return *ptr_; }
   
private:
        ConstPropPtr();
        ConstPropPtr(const ConstPropPtr&);
        ConstPropPtr& operator=(const ConstPropPtr&);
        T* ptr_;
};

这个类提供了对问题1的解决。下面会讲解,主要的点是,对象不同于指针,对象能传递const属性。
下面看下pimlT的定义:
template<class T, template<class> class Ptr = ConstPropPtr>
struct PimplT
{
        typedef T Impl;

        // declare pimpl
        typedef Pimpl<ImplT<T>, Ptr<ImplT<T> > > Type;

        // inherit pimpl
        typedef PimplOwner<ImplT<T>, Ptr<ImplT<T> > > Owner;
};
可以看到对Type
的定义,下面的owner也不说了,直接到Pimpl,

template
<   
        class T,
        typename Pointer = ConstPropPtr<T>
>
class Pimpl
{
public:

        typedef T Impl;

        Pimpl() : ptr_(new T)
        {}

        ~Pimpl()
        {
            typedef char T_must_be_defined[sizeof(T) ? 1 : -1 ];
        }


        T* operator->()
        {
            return ptr_.operator->();
        }

        T& operator*()
        {
            return ptr_.operator*();
        }

        const T* operator->() const
        {
            return ptr_.operator->();
        }

        const T& operator*() const
        {
            return ptr_.operator*();
        }

        Pointer& wrapped()
        {
            return ptr_;
        }

        const Pointer& wrapped() const
        {
            return ptr_;
        }


private:
        Pimpl(const Pimpl&);
        Pimpl& operator=(const Pimpl&);

        Pointer ptr_;
};

可以看到,这个类用了上面的ConstPropPtr从而使对象有了const属性的传递。其他的,实际上,帮你操作了new,再ConstPropPtr里释放,有点RAII的味道,剩下的完全模拟保存的指针的行为,即implT的实现,从而使其能正确的操作对象。
睡死了,早早结束
                                                 alex_yuu
posted @ 2007-02-11 15:52 agerlis 阅读(397) | 评论 (0)编辑 收藏

我的msn:agerlis@163.com
我的email:agerlis@163.com
我的qq:350457656
posted @ 2007-02-11 15:32 agerlis 阅读(322) | 评论 (3)编辑 收藏

仅列出标题