程序泛型相关说明
今天将音频的部分代码给同事,希望获得意见,结果他指出代码中有一块完全无法理解.
个人觉得这块的确值得说明一下,对于学习C++,STL,Boost的朋友都会有些好处吧.
原文代码如下:
// 播放缓冲区map表中的所有音频
void IAudioEngine::PlayAll(bool bLoop)
{
std::for_each(m_AudioBuffers.begin(), m_AudioBuffers.end(),
boost::bind(&IAudioBuffer::Play, boost::bind(select2nd<AUDIO_BUFFERS_MAP::value_type>(), _1), bLoop));
}
如果您看过我上一篇
IAudioEngine是一个音频管理引擎(或称为 音频管理器),它内部存放了一个音频对象的MAP表,
// 音效数据缓冲区的智能指针
typedef boost::shared_ptr<IAudioBuffer> SP_AUDIOBUFFERS;
typedef std::map<size_t, SP_AUDIOBUFFERS> AUDIO_BUFFERS_MAP;
AUDIO_BUFFERS_MAP m_AudioBuffers; // 保存的音频缓冲区表
具体IAudioBuffer是什么,你可以理解为一个音频文件数据,虽然这中间还有一些出入,不过已经不影响你对这段代码的理解了.
好,言归正传.我们看上面那个PlayAll中到底做了什么.
我们来分段理解,首先看
std::for_each(m_AudioBuffers.begin(), m_AudioBuffers.end(), XXXX);
这是一个使用STL库函数,它基本等同于
typedef AUDIO_BUFFERS_MAP::iterator AUDIO_BUFFERS_MAP_ITE;
for(AUDIO_BUFFERS_MAP_ITE* ite = m_AudioBuffers.begin(); ite != m_AudioBuffers.end(); ++ite )
{
ite->XXXX();
}
这样就容易理解了吧,相当于遍历整个map表,std::for_each的第三个参数应当是一个函数指针,在遍历表中每个元素时,会默认调用一次该函数进行事件处理.
那么,深入一步看XXXX的构成.boost::bind(&IAudioBuffer::Play, boost::bind(select2nd
我们先看一下boost::bind的使用.
boost::bind(&Fun, _1, _2)(x, y)
它等价于 Fun(x, y)
其中&Fun是一个函数地址,_1,_2是标志符,表示预先提供参数编号.之后_1,_2他们会和后面的参数列表对应位置的参数进行互换. 再后面的x, y是实际参数,但注意,参数列表中的x,y是必须可以获得引用的.
那么,我为什么要采用这种方式?来,想想.我们的std::for_each能够遍历全部列表,并执行第三参数的函数,但是,假若该函数有多个参数,我们该如何指示?此时,不得不使用boost::bind将该函数和其参数进行绑定.boost::bind()的实际意义就在此.
那么,我们的的boost::bind()绑定的函数地址为IAudioBuffer::Play, 后面boost::bind(select2nd
接下来我们分析boost::bind(select2nd
boost::bind(select2nd
接下来,AUDIO_BUFFERS_MAP这个Map表的value_type实际上就是Map中一组对应组,select2nd取得这个对应组的第二个参数,在本代码段的typedef std::map
所以,我们为IAudioBuffer::Play()函数传入了两个参数,一个指向IAudioBuffer的智能指针,一个bLoop,这和我在IAudioBuffer中声明的IAudioBuffer::Play(bool bLoop);是一致的,只不过,多传一份*this的指针,来确定到底是哪份IAudioBuffer调用的. 呃,希望你能知道a::fun() 实际上就是 a::fun(*this)的这个道理.
那么我们来回忆一下,我为什么这么做.
首先,我需要一个循环来对Map中的每个元素进行函数调用,但遗憾的是,我完全无法控制跌代器的跌代距离,于是采用了for_each来简化这个循环,之后由于函数带参,所以采用了boost::bind来进行参数绑定.