[C++快速开发] 查询

本文介绍C++语言中进行查询操作的高效模板。


提起查询,最先想到的应该就是find函数,find函数的定义如下:

template<class InputIterator, class T> InputIterator find(InputIterator first, InputIterator last, const T& val);

简单的说,就是确定查询的起始点和终止点及查询的内容,返回第一次找到的位置。

还是给一个例子:

查找数组中数字5第一次出现的位置。

int a[] = { 1, 3, 5, 7, 9 };
int* index = find(a, a + 5, 5);
cout << (int)(index - a) << endl;

对于数组,find的返回值是一个指针。上面的例子中,返回的指针index的值就是a数组中数字5出现位置的地址。下一行输出的是该位置的下标,结果为2。

如果查不到,返回的是指向数组后一个位置的指针。如在例子中,将查询值改为11,则输出值为5。

如果是对于string的操作,查找'a'第一次出现的位置。

string s = "Hello";
int index = s.find('a');

结果index直接就是第一次出现的位置下标。

如果是对于vector或者list等结构,调用find函数方法与数组类似,但所有的参数由数组中的指针变为相应的迭代器。

其他的如find_first_of, find_last_of, find_first_not_of 用法都与find类似,只是意义稍有不同。


对于上面的find函数,只能以一个等于特定的值作为查找条件,而不能以一个范围作为限定条件。

下面介绍find_if函数,可以理解为find函数的泛型,其定义如下:

template<class InputIterator, class Predicate> InputIterator find_if(InputIterator first, InputIterator last, Predicate pred);

这个函数也是有三个参数,前两个参数也是表示find的起始和终止位置,第三个参数一般为一个布尔类型的函数,用于给出筛选条件,满足条件的返回true。该函数的返回值的情形与find是相同的。

find函数给过数组和string的例子,这里就给一个vector的例子。查找偶数。

vector<int> a = { 1, 2, 3, 4 };
auto res = find_if(a.begin(), a.end(), [](int x) -> bool { if (x % 2 == 0) return true; else return false; });
结果res是引用第一个满足条件的元素的迭代器。如果查找不到则返回last的迭代器。与find函数相同。
在上面的例子中,find_if的第三个参数是一个lambda表达式,有关lambda的相关介绍请参考本人的另一篇文章:在C++中使用lambda ( http://blog.youkuaiyun.com/xiahn1a/article/details/41665339)

建议大家都能掌握lambda,这个对于快速开发很有帮助。如果不使用lambda,则需要单独声明一个布尔返回值的函数:

bool ret(int x)
{
	if (x % 2 == 0) 
		return true; 
	else 
		return false;
}

调用find_if时直接使用函数名即可。

auto res = find_if(a.begin(), a.end(), ret });


上面介绍的find / find_if函数可以用来做什么?指定元素定位,判断是否存在指定元素,这些都是可以的。

但经常还可以看到有很多人把它用来提取满足条件的元素,或者对满足条件的元素进行计数,而且有很多论坛的帖子也是这么教的。不可否认这可以做,但效率不一定比for循环高,而且代码不一定比直接for循环简单。可以用一些更优化的函数模板来做这些问题。


对满足条件的元素计数,可以使用count / count_if函数。其中count_if是count的泛型。

count函数的函数参数与find函数是相同的,只是返回值是满足条件的元素个数,例子就不给了。

count_if函数的参数与find_if函数相同,只是返回值是满足条件元素个数,给一个和上面相似的例子。求偶数的个数。

vector<int> a = { 1, 2, 3, 4 };
auto res = count_if(a.begin(), a.end(), [](int x) -> bool { if (x % 2 == 0) return true; else return false; });

对于提取所有满足条件的元素,可以使用copy_if函数。

还是上面的例子,提取所有偶数。

vector<int> a = { 1, 2, 3, 4 };
vector<int> b(a.size());
auto res = copy_if(a.begin(), a.end(), b.begin(), [](int x) -> bool { if (x % 2 == 0) return true; else return false; });
b.resize(distance(b.begin(), res));
for (int x : b)
	cout << ' ' << x;
可以看到相比于前面的find_if和count_if。copy_if函数多了第三个参数,表示copy到的位置。其余参数意义仍相同。

在copy的时候做了处理,先预设b的大小为a,然后resize改变b的大小。resize中使用了函数distance,即计算两个迭代器之间的元素个数差。res表示copy到的数组(例子中为b)的拷贝结束点的后一位的迭代器,即copy_if函数的返回值。
还有函数remove_copy_if,是拷贝不满足条件的元素。即原集合与copy_if拷贝后的集合的差集。remove_copy_if与copy_if函数的参数及返回值意义相同。


如果要求是查找所有满足条件的结果并做替换。此时就要用到replace / replace_if函数。replace_if是replace的泛型。

用法都是相似的,只给一个replace_if函数的例子。

还是上面的例子,将所有偶数替换为1。

vector<int> a = { 1, 2, 3, 4 };
replace_if(a.begin(), a.end(), [](int x) -> bool { if (x % 2 == 0) return true; else return false; }, 1);
可以看出copy_if函数前两个参数也是指示位置,第三个参数是判断条件,第四个参数是替换后的值。这个函数返回值为void。

从上面这么多的例子可以看出,对于查询所要做的各种操作,我们不一定全部要依赖于一个find函数来实现,这样无论是实现的开销还是代码量都是大的。C++语言已经为我们提供了这些好用的函数,我们为什么不用呢?


本文所涉及代码均在Visual Studio 2015中通过。部分代码如lambda表达式,需要支持C++ 11标准的编译器,如最新版G++及VS2012及以上版本等。

1.1 什么是Herm Herm是一套快速开发高性能的网络应用的C++库。比如开发网络游戏、即时通信、流媒体、文件下载、P2P等基于TCP/IP网络应用。 Herm包括三个组件: (1)Utilities 最基础的组件,提供线程、一读一写线程不加锁的ring buffer、二进制消息解析器、支持多态的对象管理器等。 (2)Socket 用面向对象和泛型的方法抽象了TCP/UDP的Socket IOs;抽象了Win32 Select、Linux epoll和FreeBSD kqueue的多路复用API。统一了三者水平模式(Level Triggered)的语义(一套代码在Win32/Linux/FreeBSD运行结果是一样的),Linux上也支持了边缘模式(Edge Triggered)。 (3)Framework 基于Utilities和Socket的简化开发网络应用的框架,抽象出Peer和Session对象。Peer和Session对象以及Framework实现的功能将在第2章介绍。 Herm目前仅支持Linux/Windows/FreeBSD 32bits平台。调用者可以基于不同的需求使用不用的组件。 1.2 最简单的例子 本节给出两个分别用Framework和Socket组件实现的简单TCP Server的例子。所有的例子可以参考examples frameworks和multiplexors目录。 1.2.1 用Framework实现TCP Server 首先,实现一个Listener, class Listener : public Herm::Listener { virtual void Accept(Herm::Session* session) { // 在这里得到一个于客户端通信的Session // 注册用于处理收到的消息的handler // 用Session::Push将数据写到发送buffer,最终数据传给client } }; 实现一个App, class App : public Herm::App { virtual bool Init() { // 1. 创建Network Herm::Network* net = CreateNetwork(); // 2. 创建一个TCP Server Peer,将Listener注册到Peer,进行监听 Herm::Peer* peer = net->CreateTCPServer(addr, new Listener); } }; 1.2.2 用Socket实现TCP Server 用Socket实现TCP Server更灵活,但实现者要做一些额外的工作,比如tcp stream解析,缓冲队列处理等等。 首先实现一个AcceptHandler,处理Client连接, class AcceptHandler : public Herm::EventHandler { virtual int Receive(int) { m_acceptor->Accept(streamHandler->GetStream()); g_reactor->Register(streamHandler, Herm::READ_MASK); ... } private: Herm::Acceptor* m_acceptor; }; 实现StreamHandler, 处理数据收发, class StreamHandler : public Herm::EventHandler { // Handle onle int param on FreeBSD, pls see the example of FreeBSD_tcp_server virtual int Receive(int) { m_stream->Recieve(buf, .......); ..... } private: Herm::Stream* m_stream; }; 最后,在一个线程里把上面Handler执行起来, while (true) g_reactor->Run(); 1.3 Herm地址 https://sourceforge.net/projects/speed/ 1.4 下一章话题 下章将较详细地介绍Framework组件,主要介绍如何用Framework实现真实可用的网络游戏的接入网关Server(gated)和逻辑Server(zoned) 转自优快云的herm_lib http://blog.youkuaiyun.com/herm_lib
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值