用bind+function取代虚函数在好几年前就有人提出了,曾引起广泛的讨论,有支持的有反对的,可能赞成的人占大多数。这个话题挺有趣,本来是作为技术沙龙的开放性话题来讨论的,由于时间关系并没有讨论。今天就来具体探讨一下这个问题,我将做两个实验来验证一下这两种做法,具体是实现两个模式:策略模式和责任链模式。我将分别用经典的虚函数和bind+function来实现这两个模式。通过这两个实验来得出我的结论。

实验一:策略模式的实现

1.虚函数方式实现策略模式

class Calculater
{
public:
virtual int calculate(int x, int y) = ;
}; class Minus : public Calculater
{
public:
int calculate(int x, int y)
{
return x - y;
}
}; class Plus : public Calculater
{
public:
int calculate(int x, int y)
{
return x + y;
}
}; class CalcuClient
{
private:
Calculater* m_caculater;
public:
CalcuClient(Calculater* caculater) : m_caculater(caculater){} int calculate(int x, int y)
{
return m_caculater->calculate(x, y);
}
};

2.bind+function方式实现策略模式

class NewCalcuClient
{
private:
std::function<int(int, int)> m_function; public:
NewCalcuClient(std::function<int(int, int)> function) : m_function(function){} int calculate(int x, int y)
{
return m_function(x, y);
}
};

测试代码:

    Minus minus;
CalcuClient client(&minus); Plus plus;
CalcuClient client2(&plus); int r = client.calculate(, );
int r2 = client2.calculate(, );

   //bind+function
NewCalcuClient newclient(boost::bind(&Minus::calculate, &minus, _1, _2));
NewCalcuClient newclient2(boost::bind(&Plus::calculate, &plus, _1, _2)); int r3 = newclient.calculate(, );
int r4 = newclient2.calculate(, );

  bind+function取代虚函数的一个重要理由是虚函数带来了效率损失,bind+function效率更高,我做了一个性能测试, 分别调用10000000次来看耗时,发现虚函数比bind+function方式要快一些,无论是用标准库的bind还是boost的bind,都比虚函数方式要慢,所以说bind+function比虚函数性能更好是想当然,站不住脚的。接下来看第二个实验。

实验二:责任链模式的实现

1.虚函数方式实现责任链模式

struct Request
{
int RequestType;
}; class Handler
{
protected:
std::shared_ptr<Handler> m_next;
public:
Handler(std::shared_ptr<Handler> next) : m_next(next){} virtual void HandleRequest(Request) = ;
}; class ConcreteHandler1 : public Handler
{
public:
ConcreteHandler1(std::shared_ptr<Handler> next) : Handler(next){} void HandleRequest(Request request)
{
if (request.RequestType == )
{
cout << "request handled in ConcreteHandler1" << endl;
}
else
{
if (m_next != nullptr)
m_next->HandleRequest(request);
}
}
}; class ConcreteHandler2 : public Handler
{
public:
ConcreteHandler2(std::shared_ptr<Handler> next) : Handler(next){} void HandleRequest(Request request)
{
if (request.RequestType == )
{
cout << "request handled in ConcreteHandler2" << endl;
}
else
{
if (m_next != nullptr)
m_next->HandleRequest(request);
}
}
}; class ConcreteHandler3 : public Handler
{
public:
ConcreteHandler3(std::shared_ptr<Handler> next) : Handler(next){} void HandleRequest(Request request)
{
if (request.RequestType == )
{
cout << "request handled in ConcreteHandler3" << endl;
}
else
{
if (m_next != nullptr)
m_next->HandleRequest(request);
}
}
};

2.bind+function方式实现责任链模式

class ChainHandler
{ public:
std::function<void(Request)> function; void HandleRequest(Request request)
{
function(request);
} std::function<void(Request)>& getfunction()
{
return function;
}
}; void assemble(std::function<void(Request)> call, std::function<void(Request)> next, Request request)
{
if (next != nullptr)
next(request);
else
call(request);
}

测试代码:

void Test()
{
auto thirdHandler = std::make_shared<ConcreteHandler3>(nullptr);
auto secondHandler = std::make_shared<ConcreteHandler2>(thirdHandler);
auto firstHandler = std::make_shared<ConcreteHandler1>(secondHandler); Request request = { };
firstHandler->HandleRequest(request); ChainHandler chain; std::function<void(Request)> f1 = std::bind(&ConcreteHandler1::HandleRequest, firstHandler, std::placeholders::_1);
std::function<void(Request)> f2 = std::bind(&ConcreteHandler2::HandleRequest, secondHandler, std::placeholders::_1);
std::function<void(Request)> f3 = std::bind(&ConcreteHandler3::HandleRequest, thirdHandler, std::placeholders::_1); chain.function = std::bind(&assemble, f1, chain.function, std::placeholders::_1);
chain.function = std::bind(&assemble, f2, chain.function, std::placeholders::_1);
chain.function = std::bind(&assemble, f3, chain.function, std::placeholders::_1); chain.HandleRequest(request);
}

bind+function实现责任链模式的关键代码在这里:

chain.function = std::bind(&assemble, f1, chain.function, std::placeholders::_1);
chain.function = std::bind(&assemble, f2, chain.function, std::placeholders::_1);
chain.function = std::bind(&assemble, f3, chain.function, std::placeholders::_1); chain.HandleRequest(request);

  这几行代码通过assemble不断地往function链条中加function,最后调用的时候会从链条的第一个function开始调用。

  bind+function取代虚函数的另外一个理由是松耦合,去除了继承的限制,方法的实现更加灵活,确实,低耦合确实是bind+function最大的优点,然而这个最大的优点也成了它最大的缺点,当需要替代的虚函数增多时,组装function的复杂度也在增加,太松散了导致代码也不够直观,代码的内聚性也变低了。比如上面责任链模式的实现,虚函数的实现明显比bind+function的实现要优雅。

结论

  bind+function相比虚函数的实现在性能上并不占优,最大的优点是大大降低类之间的耦合度,缺点是太过于松散导致代码的内聚性和可读性降低。

  bind+function适用的场景:

  1.迫切需要接口和实现解耦;

  2.需要解耦的接口很少。

  满足这两种情况适合用bind+function,否则还是用虚函数更好。

应该用bind+function取代虚函数吗?的更多相关文章

  1. 以boost::function和boost:bind取代虚函数

    转自:http://blog.csdn.net/Solstice/archive/2008/10/13/3066268.aspx 这是一篇比较情绪化的blog,中心思想是“继承就像一条贼船,上去就下不 ...

  2. boost::function和boost:bind取代虚函数

    以boost::function和boost:bind取代虚函数 这是一篇比较情绪化的blog,中心思想是"继承就像一条贼船,上去就下不来了",而借助boost::function ...

  3. Virtual Function(虚函数)in c++

    Virtual Function(虚函数)in c++ 用法: virtual void log() { std::cout << "hello world!" < ...

  4. C++ polymorphism Virtual Function 多态 虚函数

    Polymorphism in C++ https://www.tutorialspoint.com/cplusplus/cpp_polymorphism.htm https://github.com ...

  5. c++ virturn function -- 虚函数

    c++ virturn function -- 虚函数 pure irtual function  -- 纯虚函数   先看例子 #include <iostream> using nam ...

  6. 虚函数与bind 实现设计模式的练习

    相同模式使用虚函数与bind function进行实现对比 #include "stdafx.h" #include <iostream> #include <f ...

  7. 【深度探索c++对象模型】Function语义学之虚函数

    虚函数的一般实现模型:每一个class有一个virtual table,内含该class中的virtual function的地址,然后每个object有一个vptr,指向virtual table. ...

  8. why pure virtual function has definition 为什么可以在基类中实现纯虚函数

    看了会音频,无意搜到一个frameworks/base/include/utils/Flattenable.h : virtual ~Flattenable() = 0; 所以查了下“纯虚函数定义实现 ...

  9. 纯虚函数(pure virtual function )和抽象类(abstract base class)

    函数体=0的虚函数称为“纯虚函数”.包含纯虚函数的类称为“抽象类” #include <string> class Animal // This Animal is an abstract ...

随机推荐

  1. XmanagerEnterprise-6.0.0092-beat内测版|免费使用

    xshell很还好用,目前发布beat6.0免费注册.免费使用... 00.安装 11.扁平化的UI download: 链接: https://pan.baidu.com/s/1qXTjjAG 密码 ...

  2. 上海期货交易所CTP行情和交易接入

    发布时间:2018-09-25   技术:C++11,动态库的制作   概述 CTP的接入Demo 详细 代码下载:http://www.demodashi.com/demo/14125.html 本 ...

  3. 【TP3.2】TP3.2的 FIND_IN_SET()的用法

    1.mysql的find_in_set 用法我这里就不介绍了,很好用的一个方法. 2.TP3.2使用: $where['_string'] = 'FIND_IN_SET('."'$id'&q ...

  4. Android拦截外拨电话

    拦截监听外拨的电话,并进行处理: 向外拨打电话时系统会发出一个有序广播,虽然该广播最终会被拔号器里的广播接收者所接收并实现电话拔打,但我们可以在广播传递给拔号广播接收者之前先得到该广播,然后清除传递给 ...

  5. Windows下node.js安装及环境配置

    1. 安装 官网下载node.js的安装版,一路next,中间可以自定义安装路径 完成后安装目录内容如下 cmd下检查是否安装成功 新版Node.js已自带npm,所以安装Node.js时会一起安装, ...

  6. iOS 10 的一个重要更新-线程竞态检测工具 Thread Sanitizer

    本文介绍了 Xcode 8 的新出的多线程调试工具 Thread Sanitizer,可以在 app 运行时发现线程竞态. 想想一下,你的 app 已经近乎大功告成:它经过精良的打磨,单元测试全覆盖. ...

  7. 线程同步之mutex和event区别

    之前只是用过 关键段来对同进程不同线程进行互斥,防止对同一份资源或代码段的竞争: mutex可以理解为不同进程或者同一进程内防止对同一份资源的竞争: event更多的是同步,当然也是不同进程或者同一进 ...

  8. intellij idea 双击选中一个变量而不是单词

    在keymap 里搜索 select Word at caret ,然后双击并在弹出选项里选add mouse shortcut,然后选double click,再在下面click pad 区域点一下 ...

  9. Linux日期时间显示输出

    1.输出当前年月日 echo $(date +%F) 2014-02-21 2.输出当前时间(时分) echo $(date +%R) 12:45 3.输出当前时间(时分秒) echo $(date ...

  10. 【转】Tesla Model X的车门设计问题

    Tesla Model X的车门设计问题 Tesla即将推出的SUV(Model X),不但继承了以上提到的Model S的各种问题(触摸屏,门把,……),而且还制造了新的问题.Model X具有一个 ...