2012-07-10 77 views
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的價值觀,我注意到,密鑰仍然一致,但是我試圖指出的功能的內存地址已經消失。

我對函子沒有太多的經驗,但從我的理解來看,它能夠保留狀態,我認爲這意味着我可以在類之外調用成員函數。但我開始認爲我的成員函數因爲超出範圍而消失。

回答

2

你是對的,這是一個瓢蟲問題。在構造函數inherit中,funcAfuncB都在堆棧上分配,並在函數超出範圍時銷燬。葉子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 

但是,以避免任何內存泄漏,爲inherit析構函數將需要清理的值

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

使用newdelete可以很容易地導致內存泄漏。爲了防止他們,我會建議尋找智能點,如std::unique_ptrstd::shared_ptr。此外,在C++ 11中引入lambda表達式時,仿函數已經過時了。如果你不熟悉它們,它們非常整齊,值得研究。


如果你的編譯器支持他們,用lambda表達式做到這一點

#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 
+1

+1對於lambda表達式的引用,儘管它也許值得一提的是'std :: function',因爲這將是創建這些的'std :: map'的最簡單方法。 – Fraser 2012-07-11 01:06:50

+0

@Fraser謝謝,好主意。我已經爲lambdas添加了一個小例子。 – 2012-07-11 01:22:50

+1

不錯的一個。 (順便說一下,這裏的每個lambda都不需要'() - > int') – Fraser 2012-07-11 02:01:31

1

你是正確的 - TSpecificFunctor到外面的範圍在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_inherit->m_map仿函數不被調用。在這種情況下,你很安全,因爲你泄漏了m_inherit(它從未被銷燬)。