看板 C_and_CPP 關於我們 聯絡資訊
我剛才解決我的問題了 關於 template和 function pointer的結合 有興趣和耐心的人 可以看一下以下的程式 (ps.很長哦 非常長) 相信有寫過網路程式的人都遇過這問題,我暫用cin 來代表網路收進來的字串 class Device{ void doA(){ .... } void doB(){ .... } } void main(){ Device device; string messageFromNetwork; while(true){ cin >> messageFromNetwork; //假設我輸入 if(messageFromNetwork == "a ") device.doA(); else if(messageFromNetwork == "b") device.doB(); } } 我們通常要去用if 來判斷字串的"內容" 來決定呼叫那個函式,那有沒有辨法 不用任何if就可以做到 讓他呼叫想要的函式呢???? 以下 我就是要偷懶~我就是不想用if......... 假設 我有2種device class Device; class AngelDevice : public Device class DemonDevice : public Device -----------------------Device.h---------------------------------- class Device{ public: Device(); virtual void doAddItem(string parameter); virtual void doDelItem(string parameter); virtual void doShowItem(string parameter); virtual void execute(string command,string parameter)=0; protected: int item_number; }; ----------------------------------------------------------------- ----------------------Device.cpp--------------------------------- Device::Device(){ item_number = 0; } void Device::doAddItem(string parameter){ int num = atoi(parameter.c_str()); item_number += num; } void Device::doDelItem(string parameter){ int num = atoi(parameter.c_str()); item_number -= num; } void Device::doShowItem(string parameter){ std::cout << item_number << endl; } ----------------------------------------------------------------- 一個device有一個屬性叫 item_number代表你有的道具數量 device是一個abstract class 不能被new doAddItem 則加一個道具 doDelItem 則減一個道具 doShowItem則印出有多少道具 execute等下說明 而有2種device繼承自device 就是AngelDevice , DemonDevice -----------------------AngelDevice.h----------------------------- class AngelDevice : public Device{ typedef void (AngelDevice::*APTR)(string); public: AngelDevice(); virtual ~AngelDevice(); virtual void doAddItem(string parameter); virtual void doDelItem(string parameter); virtual void doTwiceItem(string parameter); virtual void execute(string command,string parameter); protected: Commander<AngelDevice> commander; }; ----------------------------------------------------------------- ----------------------AngelDevice.cpp----------------------------- AngelDevice::AngelDevice(){ commander.addInvocation("add",&AngelDevice::doAddItem); commander.addInvocation("del",&AngelDevice::doDelItem); commander.addInvocation("twice",&AngelDevice::doTwiceItem); commander.addInvocation("show",&AngelDevice::doShowItem); } AngelDevice::~AngelDevice(){ } void AngelDevice::doAddItem(string parameter){ int num = atoi(parameter.c_str()); item_number += num*2; } void AngelDevice::doDelItem(string parameter){ int num = atoi(parameter.c_str()); item_number -= num/2; } void AngelDevice::doTwiceItem(string parameter){ item_number*=2; } void AngelDevice::execute(string command,string parameter){ APTR ptr = commander.getInvocation(command); if(ptr) (this->*ptr)(parameter); } --------------------------------------------------------------- AngelDevice 是一種 天使的裝置,如果add item則會得到2倍 del item則只扣除一半,另外還有一個新函式叫 twiceitem 可以讓道具加倍 -------------------------DemonDevice.h------------------------ class DemonDevice : public Device{ typedef void (DemonDevice::*DPTR)(string); public: DemonDevice(); virtual ~DemonDevice(); virtual void doAddItem(string parameter); virtual void doDelItem(string parameter); virtual void doHalfItem(string parameter); virtual void execute(string command,string parameter); protected: Commander<DemonDevice> commander; }; --------------------------------------------------------------- --------------------------DemonDevice.cpp---------------------- DemonDevice::DemonDevice(){ commander.addInvocation("add",&DemonDevice::doAddItem); commander.addInvocation("del",&DemonDevice::doDelItem); commander.addInvocation("half",&DemonDevice::doHalfItem); commander.addInvocation("show",&DemonDevice::doShowItem); } DemonDevice::~DemonDevice(){ } void DemonDevice::doAddItem(string parameter){ int num = atoi(parameter.c_str()); item_number += num/2; } void DemonDevice::doDelItem(string parameter){ int num = atoi(parameter.c_str()); item_number -= num*2; } void DemonDevice::doHalfItem(string parameter){ item_number/=2; } void DemonDevice::execute(string command,string parameter){ DPTR ptr = commander.getInvocation(command); if(ptr) (this->*ptr)(parameter); } ------------------------------------------------------------- demon device是另外一種裝置,add item只能拿到一半 del item則被扣除2倍,另外還有一個half item讓道具減半 按照傳統的做法 你的main應該是長這樣 void main(){ AngelDevice device; string msgFromNet; while(true){ cin >> msgFromNet; string command = msgFromNet.substr(0,msgFromNet.find(" ")); string parameter = msgFromNet.substr(msgFromNet.find(" ")+1); // if msgFromNet = add 123 then command = add,param = 123 if(command =="add") device.doAddItem(parameter); else if(command == "del") device.doDelItem(parameter); else if(command == "show") device.doShowItem(parameter); else if(command == "twice") device.doTwiceItem(parameter); } } 為了不想看到這個if 所以 我新增了 Commander利用template + function pointer 來解決 ----------------------Commander.h------------------------------------ template <typename T> class Commander{ public: void addInvocation(string command,void (T::*fptr)(string)){ cmdMap[command] = fptr; } void (T::*getInvocation(string command))(string){ return cmdMap[command]; } protected: map<string,void (T::*)(string)> cmdMap; }; ---------------------------------------------------------------------- 這個是呼應該AngelDevice , DemonDevice 剛才建構子中的 AngelDevice : commander.addInvocation("add",&AngelDevice::doAddItem); commander.addInvocation("del",&AngelDevice::doDelItem); commander.addInvocation("twice",&AngelDevice::doTwiceItem); commander.addInvocation("show",&AngelDevice::doShowItem); DemonDevice : commander.addInvocation("add",&DemonDevice::doAddItem); commander.addInvocation("del",&DemonDevice::doDelItem); commander.addInvocation("half",&DemonDevice::doHalfItem); commander.addInvocation("show",&DemonDevice::doShowItem); 將命令字串和函式做 connect 以後 main 就如以下所示 void main(){ string command,parameter; AngelDevice device; while(true){ std::cin >> command >> parameter; device.execute(command,parameter); } } 重點是在 將 string 和 function pointer做結合,並放在map之中 接著 透過 template來完成 需要include : <iostream> <string> <map> 後記 : 在高手如雲的c++版 這也許有點野人獻曝啦 c++真的是方便 透過這樣子做 只需少許class 就可以完成 不像以前用if else 或者 把每個命令都編成class 使用Command Pattern or Decorate Pattern -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 219.70.172.147
loveme00835:mem_fun 08/07 02:23
neigence:什麼!! 竟然有這種東西可以用!!! 我覺得好丟臉= = 08/07 02:41
avhacker:太長懶得看,不過我猜boost.bind, boost.function 更合用 08/07 03:02
tinlans:effective c++ 就推薦過 boost 的 bind 跟 function 了啊 08/07 03:03
tinlans:其實環境有支援 TR1 的話也能直接用 TR1 的函式庫。 08/07 03:04
tinlans:不過手寫一遍也沒差,就當作是練習 T::* 和 ->*。 08/07 03:06
tinlans:只是你都用了 C++ 還在寫 atoi(),就不得不唸一下了。 08/07 03:06
loveme00835:雖然我也推 bind, 還是請原po都比較看看 08/07 03:06
neigence:真的很感謝樓上大大們的推文..!! 受益良多 08/07 03:07
tinlans:建議把 atoi() 改成 istringstream。 08/07 03:08
tinlans:atoi() 傳回 0 的時候很難判斷是轉錯還是真的是 0。 08/07 03:08
loveme00835:lexical_cast 也不錯 XD 08/07 03:10
tinlans:是不錯,只是它會丟 exception 讓我有點不愛用。 08/07 04:24