问题:实现一个bitmap类(为避免与stl中的bitset重名),用于位图。
测试用例:
BitMap bm(20);
bm[3] = 1;
bm[5] = 0;
bool b = bm[12];
其本质上是用内存中的一位来表示某种意义。
难点在于,使用int或者unsigned int类型保存多位bool变量(也就是0或者1)后,怎么重载下标操作符。
如果不使用下标而是使用函数,例如bm.set(3);或者bm.reset(12);一般是很简单的,只需要重载[]即可。
使用下标操作符的难点在于:c++不支持bit引用。
但是就是要求使用下标操作(项目中你可以这么做,但是面试的时候,面试官就要求使用下标),how?
这里需要使用一种设计模式:代理模式。
先看代码:
#include <iostream>
#include <cassert>
/****************************************
* 20130506 微软实习生面试时悲剧的题目
* 当时根本没想到代理模式
* 至于代理模式,以前也仅仅是听说过而已
*****************************************/
using namespace std;
/*****************************************
* BitMap bm(20);
* bm[9]=1;//set bit
* bm[1]=0;//clear
*****************************************/
class BitMap
{
typedef unsigned int UINT;
public:
BitMap(size_t bitlen=0):m_len(bitlen)
{
nUINT=m_len/DATALEN+(m_len%DATALEN==0?0:1);
m_data = new UINT[nUINT]();
}
~BitMap()
{
delete[] m_data;
}
void set(size_t index)
{
assert(0<=index&&index<m_len);
*(m_data+index/DATALEN) = *(m_data+index/DATALEN) | (1<<(index%DATALEN));
}
void reset(size_t index)
{
assert(0<=index&&index<m_len);
*(m_data+index/DATALEN) = *(m_data+index/DATALEN)&(~(1<<(index%DATALEN)));
}
bool test(size_t index)const
{
assert(0<=index&&index<m_len);
return *(m_data+index/DATALEN) & ( 1<<(index%DATALEN));
}
void flip()///~all bits
{
for(unsigned int i=0;i<nUINT;i++)
*(m_data+i) ^= (~0);
}
/**** 代理模式 ****/
class proxy
{
public:
proxy(BitMap &refbm, size_t index):
rbm(refbm),index(index)
{
}
proxy & operator= (bool val)
{
if(val)
rbm.set(index);
else
rbm.reset(index);
return *this;
}
proxy & operator= (const proxy &rhs)
{
if(rhs.rbm.test(rhs.index))
rbm.set(index);
else
rbm.reset(index);
return *this;
}
operator bool()
{
return rbm.test(index);
}
private:
BitMap &rbm;
size_t index;
};
proxy operator[](size_t index)
{
return proxy(*this,index);
}
const proxy operator[] (size_t index) const
{
return proxy(const_cast<BitMap &>(*this), index);
}
private:
UINT *m_data;///实际保存bit的存储区
size_t m_len;///位的长度,支持可变长度的位,bitset不支持。
size_t nUINT;///实际使用的UINT的个数
const static int DATALEN = 32;///每个UINT可以保存的bit个数
friend ostream& operator<< (ostream& out,BitMap &bm);///用于输出,便于调试
};
ostream& operator<< (ostream& out,BitMap &bm)
{
for(int i=bm.m_len-1;i>=0;i--)
out<<bm.test(i);
return out;
}
void testcase()
{
BitMap b(40);
cout<<b<<endl;;
b.set(2);
b.set(3);
cout<<b<<endl;
b.reset(2);
cout<<b<<endl;
b.flip();
cout<<b<<endl;
cout<<"ttt"<<endl;
b[1]=0;
cout<<b<<endl;
b[1]=1;
cout<<b[0]<<endl;
}
int main()
{
testcase();
return 0;
}
前面的set、reset、flip等函数的实现就是位运算,不需要多说。
关键在这里:
b[1]=0;
首先,b[1]将调用下标操作符,
proxy operator[](size_t index)
{
return proxy(*this,index);
}
这里返回的是一个proxy对象。proxy怎么构造的呢?2个参数:BitMap引用和index。这2个参数足以定位到一个具体的bit!也就是说,一个proxy对象对应一个bit。
然后将一个bool值赋值给这个对象,将调用proxy的赋值操作符:
proxy & operator= (bool val)
{
if(val)
rbm.set(index);
else
rbm.reset(index);
return *this;
}
这样就修改了BitMap对象的某一位。
operator bool()
{
return rbm.test(index);
}
这里定义了类型转换,可以将一个proxy对象转换为一个bool变量。读取的时候,例如bool b=bm[2];将调用这个函数。
这样,对外看来,使用下标访问的就是BitMap对象中的某一位,可以进行读或者写的操作。
很巧妙的设计。
/*********************************************分****割****线***************************************************************/
关于代理模式:
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
直接看这些定义,感觉理解起来很不方便。
有了这个例子,体会得深的多了。
有人说:
“设计模式就是编程思想,很多新手觉得好像都看懂了但不会用,因为编程思想还没达到那个层次!
这个要靠点点滴滴的积累,当你积累了10W行有效代码,再回过头去看设计模式,自然会有豁然开朗的感觉
框架要用,“不要重复造轮子”,但是要关注框架的实现原理”
深有体会!