C++ 的 idioms(三)
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.
[...] terminal 类型 eval 的特化。这样其他的部分就使用默认的实现即可(CRTP 那个例子里面如果去掉子类的实现就会调用父类。)于是我们就能比较清楚的理解 [...]
proto 高级篇 « demonstrate 的 blog
2012/03/18 at 12:50 AM