C++的另一种抽象流派:bind+function
C++一直提供了基于多态的抽象设计方式,让我们摆脱了C语言中type+callback+void*抽象方式,从而可以将数据与方法封装在一起,更清晰的描述代码。
C++的抽象基于多态实现,一个简单的例子:
#include <iostream> class A { public: virtual ~A() { /* std::cout << __FUNCTION__ << std::endl; */ } virtual void Func(void) = 0; }; class B: public A { public: virtual ~B() { std::cout << __FUNCTION__ << std::endl; } virtual void Func(void) { std::cout << "B::Func" << std::endl; } }; class C: public A { public: virtual ~C() { std::cout << __FUNCTION__ << std::endl; } virtual void Func(void) { std::cout << "C::Func" << std::endl; } }; void IDoNotKnowAnyDetail(A *base) { base->Func(); delete base; } int main(int argc, char *const argv[]) { A *pb = new B(); A *pc = new C(); IDoNotKnowAnyDetail(pb); IDoNotKnowAnyDetail(pc); return 0; }
输出:
B::Func
~B
C::Func
~C
A是抽象基类, B和C分别实现Func, 然后有一个IDoNotKnowAnyDetail函数依赖了A抽象, 对细节不知情.
这是目前C++, 无论是经典教材还是大学教材里都讲的基础知识, 那么bind+function如何达到类似的抽象能力呢, 看下面的代码:
#include <iostream> #include <algorithm> #include <tr1/memory> #include <tr1/functional> class B { public: ~B() { std::cout << __FUNCTION__ << std::endl; } void FuncB(void) { std::cout << __FUNCTION__ << std::endl; } }; class C { public: ~C() { std::cout << __FUNCTION__ << std::endl; } void FuncC(void) { std::cout << __FUNCTION__ << std::endl; } }; void IDoNotKnowAnyDetail(std::tr1::function<void ()> func) { func(); } int main(int argc, char *const argv[]) { std::tr1::function<void ()> func_b = std::tr1::bind(&B::FuncB, std::tr1::shared_ptr<B>(new B())); std::tr1::function<void ()> func_c = std::tr1::bind(&C::FuncC, std::tr1::shared_ptr<C>(new C())); IDoNotKnowAnyDetail(func_b); IDoNotKnowAnyDetail(func_c); return 0; }
输出:
FuncB
FuncC
~C
~B
代码尽量和上面实现相似, 以便比较. 可以看到IDoNotKnowAnyDetail函数的参数是function<void ()>, 但它依旧表现出了与多态实现一样的输出, 与多态版本相比还少实现了一个类A, 很有意思.
bind+function版本我使用了shared_ptr取代了raw指针, 在多线程环境下线程间传递func_b, func_c是可以无需考虑内存释放的, 对象会随着function的析构而一起释放, 这一点也比较实用.
两种风格在完成工作的能力方面暂时没感觉到什么差别, 但bind+function是一种回归C风格的方案, 但同时保留了数据与方法的封装性, 并不是一种倒退.
性能方面, 多态风格由于虚函数的原因, 性能稍有损耗但根本不足为患, bind+function风格性能妥妥的, 同时避免了多态引入的继承体系, 一定程度降低代码复杂度.
可读性方面, 我认为多态实现更易读, 因为它基于面向对象的语义, 还是符合人类习惯的, bind+function风格导致多个实现类彼此没有明显关联, 也许会稍微阻碍一下理解.
实现方面, bind+function的实现是比较高端, 照我的意思说是挺暗黑的(C++这方面的难度与复杂度我真无心深究了), 但并不耽误使用, 毕竟C++11已经纳入标准, 即便我们不愿意去理解底层实现, 但一个定义和行为明确的黑盒利器依旧是我们值得选择的. 多态实现就不多说了, 大家都有经验.
代码风格方面, 我认为还是统一而不要混用, 否则对项目维护可能是个小灾难.