2

我希望能够从具有相同通用语法和基类的不同类中调用一些成员函数。类似的东西

class A: public BaseClass
{
public:
    A();
    ~A();

    int DoFoo();
    int DoBar();
    int DoBarBar();
};

class B : public BaseClass
{
public:
    B();
    ~B();

    int DoSomething();
    int DoSomethingElse();
    int DoAnother();
};

我可以将两个类中的成员函数放入一个映射中,这样我就可以拥有类似的东西

 key           value
"Option1"     *DoFoo()
"Option2"     *DoSomething()
"Option3"     *DoFoo()
 ...            ...
"Option6"     *DoAnother()

无论函数属于哪个类,我都可以根据我选择的选项调用函数以返回值。

通过一些搜索,我尝试实现自己的映射函子集。但是,映射保留了函子的地址,但其中的函数变为空。

这是我的函子声明,其中存储了一个类对象和一个函数指针

    #include <stdio.h>
    #include <vector>
    #include <algorithm>
    #include <map>
    #include <string>

//////////////////////////////////////////////////////////////
//Functor Classes
////////////////////////////////////////////////////////////// 
    class TFunctor
    {
    public:
      virtual void operator()()=0;  // call using operator
      virtual int Call()=0;        // call using function
    };


    // derived template class
    template <class TClass> class TSpecificFunctor : public TFunctor
    {
    private:
      int (TClass::*fpt)();   // pointer to member function
      TClass* pt2Object;                  // pointer to object

    public:

      // constructor - takes pointer to an object and pointer to a member and stores
      // them in two private variables
      TSpecificFunctor(TClass* _pt2Object, int(TClass::*_fpt)())
         { pt2Object = _pt2Object;  fpt=_fpt; };

      // override operator "()"
      virtual void operator()()
       { (*pt2Object.*fpt)();};              // execute member function

      // override function "Call"
      virtual int Call()
        {return (*pt2Object.*fpt)();};             // execute member function
    };

    typedef std::map<std::string, TFunctor*> TestMap;

//////////////////////////////////////////////////////////////
//Test Classes
//////////////////////////////////////////////////////////////
//Base Test class 
class base
{
public:
    base(int length, int width){m_length = length; m_width = width;}
    virtual ~base(){}


    int area(){return m_length*m_width;}

    int m_length;
    int m_width;
};

//Inherited class which contains two functions I would like to point to
class inherit:public base
{
public:
    inherit(int length, int width, int height);
    ~inherit();
    int volume(){return base::area()*m_height;}
    int area2(){return m_width*m_height;}

    int m_height;
    TestMap m_map;
};

我的继承类构造函数如下所示:

inherit::inherit(int length, int width, int height):base(length, width)
{
    m_height = height;
    TSpecificFunctor<inherit> funcA(this, &inherit::volume);
    m_map["a"] = &funcA;

    TSpecificFunctor<inherit> funcB(this, &inherit::area2);
    m_map["b"] = &funcB;
}

这是我将两个函数映射到地图的地方。就内存地址和函数指针而言,上述函数看起来还不错。

然后我尝试在一个新类中创建一个继承实例......

class overall
{
public:
    overall();
    ~overall(){}

    inherit *m_inherit;
    TestMap m_mapOverall;
};
overall::overall()
{
    m_inherit = new inherit(3,4,5);

    TestMap tempMap = m_inherit->m_map;
    int i = 0;
}

在这里,当我查看 m_inherit->m_map 的值时,我注意到键仍然是一致的,但是我试图指向的函数的内存地址已经消失了。

我对仿函数没有太多经验,但据我了解,它能够保留状态,我认为这意味着我可以在其类之外调用成员函数。但是我开始认为我的成员函数消失了,因为它超出了范围。

4

2 回答 2

2

你是对的,这是一个独家问题。在inherit构造函数中,funcA并且funcB都在堆栈上分配并在函数超出范围时销毁。m_map带有陈旧指针的叶子。

你真正想要的是

inherit::inherit(int lenght, int width, int height) :base(length, width)
{
    m_height = height;

    // allocated on the heap
    TSpecificFunctor<inherit> * funcA = new TSpecificFunctor<inherit>(this, &inherit::volume);
    m_map["a"] = funcA;

    // allocated on the heap
    TSpecificFunctor<inherit> * funcB = new TSpecificFunctor<inherit>(this, &inherit::area2);
    m_map["b"] = funcB;
} // when this function exits funcA and funcB are not destroyed

但是,为了避免任何内存泄漏,析构函数 forinherit将需要清理这些值

inherit::~inherit()
{
    for(TestMap::iterator it = m_map.begin(); it != m_map.end(); ++it) {
        delete it->second;
    }
}

使用newanddelete很容易导致内存泄漏。为了防止它们,我建议研究像std::unique_ptrand之类的智能点std::shared_ptr。此外,随着 C++11 中 lambda 的引入,仿函数也变得过时。如果您不熟悉它们,它们真的很整洁,值得研究。


如果您的编译器支持它们,请使用 lambdas 执行此操作

#include <functional>

// ...

typedef std::map<std::string, std::function<int(void)>> TestMap;

// ...

inherit::inherit(int length, int width, int height):base(length, width)
{
    m_height = height;
    m_map["a"] = [this]() -> int { return volume(); };
    m_map["b"] = [this]() -> int { return area2(); };

    // can be called like so
    m_map["a"]();
    m_map["b"]();
}

// No need to clean up in destructors
于 2012-07-11T01:03:43.633 回答
1

你是对的 - TSpecificFunctors 在 ' 构造函数的末尾超出范围inherit,所以你不应该保留指向它们的指针。

如果可以的话,更喜欢智能指针,例如

#include <memory>

...

typedef std::map<std::string, std::shared_ptr<TFunctor>> TestMap;

...

inherit::inherit(int length, int width, int height):base(length, width)
{
    m_height = height;
    m_map["a"] = std::shared_ptr<TSpecificFunctor<inherit>>(
        new TSpecificFunctor<inherit>(this, &inherit::volume));
    m_map["b"] = std::shared_ptr<TSpecificFunctor<inherit>>(
        new TSpecificFunctor<inherit>(this, &inherit::area2));
}

然后,您主要关心的是确保在销毁m_inherit->m_map后不调用仿函数,m_inherit否则您将获得未定义的行为。在这种情况下,你是安全m_inherit的,因为你泄漏了(它永远不会被破坏)。

于 2012-07-11T01:04:16.363 回答