C++ 實現 根據字串 呼叫同名函式
需求: 希望根據使用者的輸入呼叫同名的函式。
因為不想寫各種 if else,所以就建立一個key為string,value為函式指標的map,根據string的值呼叫相應的函式。
以下程式碼在gcc 3.4.6下測試通過。
下面是程式碼的第一次實現:
<code>
#include<iostream>
#include<map>#include<string>
void buildMap();
class Image{
public:
Image() {}
void func1() { std::cout << "func1 called.\n"; }
void func2() { std::cout << "func2 called.\n"; }
};
typedef void (Image::*pfunc)(); // define function pointer type
std::map<std::string, pfunc> strFuncMap;
void buildMap() { strFuncMap["func1"] = &Image::func1; strFuncMap["func2"] = &Image::func2; }
void callFunc(Image& img, const std::string& str) { if(strFuncMap.count(str)) (img.*strFuncMap[str])(); else std::cout << "unsupported function str.\n";}
int main() {
Image img;
buildMap();
callFunc(img, "func1");
callFunc(img, "func3");
return 0;
}
</code>
但是這樣用的是一些自由函式和變數,我想把它們放到類裡面去。因為我覺得不需要每一個物件都建立一個map,所以把該map設定成了static型別。
注意加紅的部分 (this->*(strFuncMap[str]))() ,是利用該map進行函式呼叫的部分,試了好多種方式,只有這樣才沒有報編譯錯誤。
另外,還有std::map<std::string, Image::pfunc> Image::strFuncMap;
<code>
class Image{
public:
typedef void (Image::*pfunc)();
static void buildMap();
Image() {}
void callFunc(const std::string& str) { if(strFuncMap.count(str))
(this->*(strFuncMap[str]))(); else std::cout << "unsupported function " << str << std::endl;}
void func1() { std::cout << "func1 called.\n"; }
void func2() { std::cout << "func2 called.\n"; }
private:
static std::map<std::string, pfunc> strFuncMap;
};
std::map<std::string, Image::pfunc> Image::strFuncMap;
void Image::buildMap() { strFuncMap["func1"] = &Image::func1; strFuncMap["func2"] = &Image::func2; }
int main() {
Image img;
img.buildMap();
img.callFunc("func2");
img.callFunc("func1");
img.callFunc("func3");
return 0;
}
</code>
但是,又有新問題,我想加入繼承和多型的功能,讓Image作為基類使用,使用者可以使用原來藉口呼叫到合適的函式。
最開始的時候我是這樣想的,把想要變成virtual的成員函式變成virtual,然後把buildMap改為非static函式並放在基類和派生類的建構函式中,把buildMap函式的內容從
void Image::buildMap() { strFuncMap["func1"] = &Image::func1; strFuncMap["func2"] = &Image::func2; }
改成
void Image::buildMap() { strFuncMap["func1"] = &func1; strFuncMap["func2"] = &func2; }
因為在我的理解裡Image::func1就會確定是呼叫的基類的函式,然後我就幻想著去掉作用域的符合buildMap自己能夠儲存到正確版本的函式。結果g++不給面子,報錯了。
ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.
也就是不允許這種寫法。
然後我想那就把buildMap也改成virtual型別,然後把對它的呼叫放在建構函式中。(雖然effective c++中建議不要在建構函式中呼叫virtual函式,但是即使基類的建構函式中呼叫的buildMap不會下降到派生類的buildMap,我會在派生類的建構函式中再呼叫派生類版本的buildMap,所以我猜應該可以的。)將它在派生類中的定義改成(假設AdvancedImage為派生類,且func1為virtual函式):
void Image::buildMap() { strFuncMap["func1"] = &AdvancedImage::func1; strFuncMap["func2"] = &Image::func2; }
事實證明這樣也是不行的,g++表示:
cannot convert `void (AdvancedImage::*)()' to `void (Image::*)()' in assignment
啊,那就是說基類和派生類的成員函式型別不同,不能賦值。
它表示成員函式的指標是很特殊的東西,支援多型。啊哈,也就是說,&Image::func1這個東西不是指代基類的fun1,而是自己可以匹配到合適的派生類函式?
所以,第三次程式碼如下:
<code>
class Image{ public: typedef void (Image::*pfunc)(); void buildMap(); Image() { buildMap(); } void callFunc(const std::string& str) { if(strFuncMap.count(str)) (this->*(strFuncMap[str]))(); else std::cout << "unsupported function " << str << std::endl;} virtual void func1() { std::cout << "base class func1 called.\n"; } void func2() { std::cout << "func2 called.\n"; } protected: static std::map<std::string, pfunc> strFuncMap; }; std::map<std::string, Image::pfunc> Image::strFuncMap; void Image::buildMap() { strFuncMap["func1"] = &Image::func1; strFuncMap["func2"] = &Image::func2; } class AdvancedImage : public Image{ public: virtual void func1() {std::cout << "drived class func1 called.\n"; } }; int main() { Image img; img.callFunc("func2"); img.callFunc("func1"); img.callFunc("func3"); AdvancedImage advImg; advImg.callFunc("func2"); advImg.callFunc("func1"); advImg.callFunc("func3"); Image *pImg = &advImg; pImg->callFunc("func2"); pImg->callFunc("func1"); pImg->callFunc("func3"); return 0; }
</code>
結果果然可以輸出正確的結果了:
func2 called. base class func1 called. unsupported function func3 func2 called. drived class func1 called. unsupported function func3 func2 called. drived class func1 called. unsupported function func3
雖然實現了功能,但是感覺還是暈暈乎乎一無所知的感覺。可能是因為不理解面向物件技術實現原理吧。