7. How to access members of the class inside a member function? Explanation: The members of a class can be used directly inside a member function. We can use this pointer when there is a conflict between data members of class and arguments/local function variable names.
In addition to holding data, classes (and structs) can also contain functions! Functions defined inside of a class are called member functions (or sometimes methods). Member functions can be defined inside or outside of the class definition.
The short answer is simply: because this is how C++ works. Static functions, by definitions, are not class methods, and are not a method on any object. Therefore you don't need objects to invoke them.
Explanation: It is always advised that the member functions should be kept public so that those functions can be used from out of the class. This is usually done to ensure that the features provided by the class can be used at its maximum.
A non-static member function must be called with an object. That is, it always implicitly passes "this" pointer as its argument.
Because your std::function
signature specifies that your function doesn't take any arguments (<void(void)>
), you must bind the first (and the only) argument.
std::function<void(void)> f = std::bind(&Foo::doSomething, this);
If you want to bind a function with parameters, you need to specify placeholders:
using namespace std::placeholders;
std::function<void(int,int)> f = std::bind(&Foo::doSomethingArgs, this, std::placeholders::_1, std::placeholders::_2);
Or, if your compiler supports C++11 lambdas:
std::function<void(int,int)> f = [=](int a, int b) {
this->doSomethingArgs(a, b);
}
(I don't have a C++11 capable compiler at hand right now, so I can't check this one.)
Either you need
std::function<void(Foo*)> f = &Foo::doSomething;
so that you can call it on any instance, or you need to bind a specific instance, for example this
std::function<void(void)> f = std::bind(&Foo::doSomething, this);
If you need to store a member function without the class instance, you can do something like this:
class MyClass
{
public:
void MemberFunc(int value)
{
//do something
}
};
// Store member function binding
auto callable = std::mem_fn(&MyClass::MemberFunc);
// Call with late supplied 'this'
MyClass myInst;
callable(&myInst, 123);
What would the storage type look like without auto? Something like this:
std::_Mem_fn_wrap<void,void (__cdecl TestA::*)(int),TestA,int> callable
You can also pass this function storage to a standard function binding
std::function<void(int)> binding = std::bind(callable, &testA, std::placeholders::_1);
binding(123); // Call
Past and future notes: An older interface std::mem_func existed, but has since been deprecated. A proposal exists, post C++17, to make pointer to member functions callable. This would be most welcome.
Unfortunately, C++ does not allow you to directly get a callable object referring to an object and one of its member functions. &Foo::doSomething
gives you a "pointer to member function" which refers to the member function but not the associated object.
There are two ways around this, one is to use std::bind
to bind the "pointer to member function" to the this
pointer. The other is to use a lambda that captures the this
pointer and calls the member function.
std::function<void(void)> f = std::bind(&Foo::doSomething, this);
std::function<void(void)> g = [this](){doSomething();};
I would prefer the latter.
With g++ at least binding a member function to this will result in an object three-pointers in size, assigning this to an std::function
will result in dynamic memory allocation.
On the other hand, a lambda that captures this
is only one pointer in size, assigning it to an std::function
will not result in dynamic memory allocation with g++.
While I have not verified this with other compilers, I suspect similar results will be found there.
You can avoid std::bind
doing this:
std::function<void(void)> f = [this]-> {Foo::doSomething();}
You can use functors if you want a less generic and more precise control under the hood. Example with my win32 api to forward api message from a class to another class.
#include <windows.h>
class IListener {
public:
virtual ~IListener() {}
virtual LRESULT operator()(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
};
#include "IListener.h"
template <typename D> class Listener : public IListener {
public:
typedef LRESULT (D::*WMFuncPtr)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
private:
D* _instance;
WMFuncPtr _wmFuncPtr;
public:
virtual ~Listener() {}
virtual LRESULT operator()(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) override {
return (_instance->*_wmFuncPtr)(hWnd, uMsg, wParam, lParam);
}
Listener(D* instance, WMFuncPtr wmFuncPtr) {
_instance = instance;
_wmFuncPtr = wmFuncPtr;
}
};
#include <map>
#include "Listener.h"
class Dispatcher {
private:
//Storage map for message/pointers
std::map<UINT /*WM_MESSAGE*/, IListener*> _listeners;
public:
virtual ~Dispatcher() { //clear the map }
//Return a previously registered callable funtion pointer for uMsg.
IListener* get(UINT uMsg) {
typename std::map<UINT, IListener*>::iterator itEvt;
if((itEvt = _listeners.find(uMsg)) == _listeners.end()) {
return NULL;
}
return itEvt->second;
}
//Set a member function to receive message.
//Example Button->add<MyClass>(WM_COMMAND, this, &MyClass::myfunc);
template <typename D> void add(UINT uMsg, D* instance, typename Listener<D>::WMFuncPtr wmFuncPtr) {
_listeners[uMsg] = new Listener<D>(instance, wmFuncPtr);
}
};
class Button {
public:
Dispatcher _dispatcher;
//button window forward all received message to a listener
LRESULT onMessage(HWND hWnd, UINT uMsg, WPARAM w, LPARAM l) {
//to return a precise message like WM_CREATE, you have just
//search it in the map.
return _dispatcher[uMsg](hWnd, uMsg, w, l);
}
};
class Myclass {
Button _button;
//the listener for Button messages
LRESULT button_listener(HWND hWnd, UINT uMsg, WPARAM w, LPARAM l) {
return 0;
}
//Register the listener for Button messages
void initialize() {
//now all message received from button are forwarded to button_listener function
_button._dispatcher.add(WM_CREATE, this, &Myclass::button_listener);
}
};
Good luck and thank to all for sharing knowledge.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With