C++ 实现 根据字符串 调用同名函数

来源:互联网 发布:兄弟连细说php视频教程 编辑:程序博客网 时间:2024/06/06 19:12

需求: 希望根据用户的输入调用同名的函数。


因为不想写各种 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, "func2");
    callFunc(img, "func1");
    callFunc(img, "func3");
    return 0;

}

</code>


但是这样用的是一些自由函数和变量,我想把它们放到类里面去。因为我觉得不需要每一个对象都建立一个map,所以把该map设置成了static类型。

注意加红的部分  (this->*(strFuncMap[str]))() ,是利用该map进行函数调用的部分,试了好多种方式,只有这样才没有报编译错误。

另外,还有std::map<std::string, Image::pfunc> Image::strFuncMap;  即使不初始化也不能删除,否则会有连接错误。这表示对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
啊,那就是说基类和派生类的成员函数类型不同,不能赋值。
额,那肿么办呢。然后我搜到了 http://www.99inf.net/SoftwareDev/VC/39737.htm 
它表示成员函数的指针是很特殊的东西,支持多态。啊哈,也就是说,&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 func3func2 called.drived class func1 called.unsupported function func3func2 called.drived class func1 called.unsupported function func3


虽然实现了功能,但是感觉还是晕晕乎乎一无所知的感觉。可能是因为不理解面向对象技术实现原理吧。

原创粉丝点击