問題:實現一個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行有效代碼,再回過頭去看設計模式,自然會有豁然開朗的感覺
架構要用,“不要重複造輪子”,但是要關注架構的實現原理”
深有體會!