demonstrate 的 blog

daily blog

C++ 的 idioms(三)

with one comment

attach by initialization

这个 idiom 希望解决的是谬 main 的情况下如何在进 framework 之前(如 MFC)执行一些 code 或者初始化一些对象,使用的技巧就是全局对象或者 static 对象,它们在 main 执行前被初始化,因此可以利用他们做一些 pre-framework 的事情。需要说明一点的是,最好不要让这些 object 出现依赖关系,因为他们的 initialization 顺序并不总是 well-defined 的。

attorney-client

这个比喻其实是表示对 friend 访问控制的限制:一个复杂的类可能有许多私有的成员、方法并不乐意暴露给其他对象,但是对特定的类型,它可以选择公开与否,那就是 friend 的作用。可是 friend 会造成“一览无余”,怎么样提供部分的 view 呢?那么自然就是提供自己的 attorney,让它选择性的暴露某些接口即可。

我们拿 boost.iterators 中的 boost::iterator_core_acess 为例。通过 boost::iterator_facade 我们可以非常容易的写一些 STL-compliant 的 iterator:

class iterator_core_access {
public:
  template <class I, class V, class TC, class R, class D> friend class iterator_facade ;
  // more about operators...
  template <class Facade> static typename Facade::reference
  dereference(Facade const& f) {
    return f.dereference();
  }
  // more like this, e.g. increment
} ;

注意,根据 boost.iterators 的文档,我们实现 iterator 的时候继承了 boost::iterator_facade<my_iterator, ValueType, tag>,同时为了能让 private 里面的实现被访问到,我们必须在 my_iterator 里面声明 friend 有一个是 boost::iterator_core_access。这里我们的 iterator 就是 client,通过设定好的 attorney 让它将这些 iterator 常用行为提供给 iterator_facade 中封装好的标准行为来使用。

这里稍微分析一下 CRTP 在这里发挥的作用。

class base {
public:
  void
  interface () {
    std::cout << "interface: " ;
    internal () ;
    std::cout << std::endl ;
  }

protected:
  virtual void
  internal () {
    std::cout << "from internal" ;
  }
} ;

class derived : public base {
protected:
  virtual void
  internal () {
    std::cout << "from derived internal" ;
  }
} ;

// somewhere in main
  {
    derived it ;
    it.interface () ;
  }

输出大家都知道,这是典型的 template methods,在 interface 里面嵌入需要子类特化的方法。问题是这引入了额外的开销,即 virtual function call。CRTP 就是为了获得静态的 polymorphism 而设计出来的奇特用法:

template <class T> class crtp_base ;

class attorney {
  template <class T> friend class crtp_base ;

  template <class CRTP>
  static inline void internal (CRTP* t) {
    t->internal () ;
  }
} ;

template <class C>
class crtp_base {
public:
  void interface () {
    std::cout << "interface: " ;
    attorney::internal (static_cast<C*> (this)) ;
    std::cout << std::endl ;
  }
private:
  friend class attorney ;
  void internal () {
    std::cout << "from internal" ;
  }
} ;

class crtp_derived : public crtp_base<crtp_derived> {
  friend class attorney ;

  void internal () {
    std::cout << "from derived internal" ;
  }
} ;

// somewhere in main
  {
    crtp_derived it ;
    it.interface () ;
  }

有趣的就是这里的 static_cast 是可用的,为什么呢?我们代入一下:当 C = crtp_derived 的时候,我们要求将 this 转换到 crtp_derived,而 crtp_derived 的确是 crtp_derived。没有那个 static_cast 行不行呢?很明显不行,那个时候,传递过去的虽然是合法的指针,但是却是 crtp_base<crtp_derived> 的,调用的自然是那里故意多写的 internal 函数(实际不会放个那个 nc 的函数在那里的)。另外,我们这里使用了 attorney,它对别的什么都不负责,仅对 friend 负责绑定 internal 方法。如果没有 attorney,如果希望使用 CRTP,implementation 也必须是 public 的,这在 iterator 的例子里面是不合适的。

我们在后面 CRTP 的 idiom 里面将会继续探讨 CRTP 的一些用途。

counted body

所谓 counted body 是指 reference counting,这个在许多 GC 语言的实现里面大量的用到了,如 python,不少的库里面也包含类似的封装,如 OpenCV 2.x 提供的 cv::Ptr 或者 cv::Mat 本身。这个想法的核心就是为每个 object 同时配备一个 counter,用来记录当前“引用”这个物体的次数,当没有人引用的时候就可以释放它了。boost 对这个 pattern 的实现就是 boost::shared_ptr 这个智能指针了。类似的还有 boost::shared_array,这两者对应 new T 和 new T[] 两种方式。实现这类 pattern 的核心在于

  • 将普通的指针存放起来,初始化引用数为 1;
  • copy constructor 或者 operator= 的时候(还有很多类似的略去)修改引用数

有了这些 idea 自己实现一个主要考究的是细心了。

——————
And he brought back all the goods, and also brought again his brother Lot, and his goods, and the women also, and the people.

Written by zt

2012/02/19 at 10:30 AM

One Response

Subscribe to comments with RSS.

  1. [...] terminal 类型 eval 的特化。这样其他的部分就使用默认的实现即可(CRTP 那个例子里面如果去掉子类的实现就会调用父类。)于是我们就能比较清楚的理解 [...]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.