Reputation: 1757
I'd like to be able to call some member functions from different classes which all have the same general syntax and base class. Something along the lines of
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();
};
Where I could potentially places the member functions from both classes into one map so that I could have something like
key value
"Option1" *DoFoo()
"Option2" *DoSomething()
"Option3" *DoFoo()
... ...
"Option6" *DoAnother()
Where I could call a function to return a value based on what option I chose, regardless of what class the function belongs to.
Through some searching, I tried to implement my own mapped set of functors. However, the map retains the address of the functor, but the functions within become null.
Here are my functor declarations which store a class object and a function pointer
#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;
};
where my inherit class constructor looks like:
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;
}
Which is where I am mapping two functions into a map. Things still look okay in the above function in terms of memory address and function pointers.
I then try to create an instance of inherit in a new class...
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;
}
Here when I look at the values of m_inherit->m_map, I notice that the keys are still consistent, however the memory addresses of the functions which I tried to point to have disappeared.
I haven't had much experience with functors but from my understanding, it is able to retain states, which I assume means that I can call member functions outside of its class. But I'm starting to think that my member functions disappear because it is out of scope.
Upvotes: 2
Views: 684
Reputation: 1400
You are right, it is a scooping issue. In the inherit
constructor, funcA
and funcB
are both allocated on the stack and destroyed once the function goes out of scope. The leaves m_map
with stale pointers.
What you really want is something like
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
But, to avoid any memory leaks, the destructor for inherit
will need to clean up the values
inherit::~inherit()
{
for(TestMap::iterator it = m_map.begin(); it != m_map.end(); ++it) {
delete it->second;
}
}
Using new
and delete
can easily lead to memory leaks. To prevent them, I would suggest looking into smart points like std::unique_ptr
and std::shared_ptr
. Also, functors are becoming obsolete with the introduction of lambdas in C++11. They are really neat and worth looking into if you are not familiar with them.
If your compiler supports them, to do this with 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
Upvotes: 2
Reputation: 78280
You're right - the TSpecificFunctor
s go out of scope at the end of inherit
's constructor, so you shouldn't keep pointers to them.
If you can, prefer smart pointers, e.g.
#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));
}
Your main concern then is to ensure that the functors in m_inherit->m_map
are not invoked after m_inherit
is destroyed or you will get undefined behaviour. In this case, you're safe since you leak m_inherit
(it's never destroyed).
Upvotes: 1