What Is the Practical Use of Pointers to Member Functions

What is the practical use of pointers to member functions?

There are many practical uses. One that comes to my mind is as follows:

Assume a core function such as below (suitably defined myfoo and MFN)

void dosomething(myfoo &m, MFN f){   // m could also be passed by reference to 
// const
m.*f();
}

Such a function in the presence of pointer to member functions, becomes open for extension and closed for modification (OCP)

Also refer to Safe bool idiom which smartly uses pointer to members.

On the use (and usefulness) of a pointer to member function vs directly calling the member function

Depending on what will vary in the future, you can decide:

  • Other functions will be called by the variable's FireCallback => a pointer-to-mf may be useful, but other mechanisms are more c++ish
  • Only the 'Callback' function is going to be called => stick with calling arg->CallBack() directly.

A possible solution to the problem is using an interface layer: this is no more than an implementation of the Observer pattern. This is a little more OO, hence verbose, but the syntax is way easier.

class Observer {
public:
virtual ~Observer(){};
virtual void callback( int v ) = 0;
};

// actual implementation
class MyCallbackObserver : public Observer {
virtual void callback( int v ) { std::cout << v << std::endl; }
void some_other_method( int v ) { std::cout << "other " << v ; }
};

And your Variable class would have a container full of observers:

class Variable {
public:
std::vector<Observer*> observers; // warning: encapsulation omitted
void FireCallback(){
// assuming C++ 0x
for( auto it : observers ) {
(*it)->Callback( value );
}
}

If other functions need to be called on the same object, you may introduce a wrapper:

class OtherCaller: public Observer {
public:
MyObserver* obs;
virtual void callback( int v ) { obs->some_other_method( v ); }
}

And add it to the collection:

Variable var;
MyObserver m;
OtherCaller mo;
mo.obs = &m;

var.observers.push_back(&m);
var.observers.push_back(&mo);

var.FireCallback();

C++: Using function pointers with member functions

You can pass function pointers to member functions. But that is not what your code is doing. You are confused between regular function pointers (void (*passedFunction)() is a regular function pointer) and pointers to member functions (&myClass::functionToPass is a pointer to a member function). They are not the same thing and they are not compatible.

You can rewrite your code like this

void myClass::function1(void (myClass::*passedFunction)())
{
(this->*passedFunction)();
}

Now your code is using pointers to member functions, but of course this means you won't be able to pass a regular function pointer.

Member functions returning pointers to member functions

Blatently ripping off the solution at GotW and adapting it to member functions:

class Foo {
public:
struct FuncPtrRet;
typedef FuncPtrRet(Foo::*FuncPtr)();
struct FuncPtrRet {
FuncPtrRet(FuncPtr pp) : p(pp) { }
operator FuncPtr() { return p; }
FuncPtr p;
};
FuncPtrRet s1() { return &Foo::s2; }
FuncPtrRet s2() { return &Foo::s3; }
FuncPtrRet s3() { return &Foo::s3; }
};

int main() {
Foo test;
Foo::FuncPtr state = test.s1();
state = (test.*state)();
state = (test.*state)();
state = (test.*state)();
state = (test.*state)();
state = (test.*state)();
return 0;
}

This seems to work on Clang 3.3. I don't think returning 0 is a good choice for an idle state (s3 here), but I might be wrong there. Having an idle state that returns itself seems more intuitive to me.

Why should I use a pointer rather than the object itself?

It's very unfortunate that you see dynamic allocation so often. That just shows how many bad C++ programmers there are.

In a sense, you have two questions bundled up into one. The first is when should we use dynamic allocation (using new)? The second is when should we use pointers?

The important take-home message is that you should always use the appropriate tool for the job. In almost all situations, there is something more appropriate and safer than performing manual dynamic allocation and/or using raw pointers.

Dynamic allocation

In your question, you've demonstrated two ways of creating an object. The main difference is the storage duration of the object. When doing Object myObject; within a block, the object is created with automatic storage duration, which means it will be destroyed automatically when it goes out of scope. When you do new Object(), the object has dynamic storage duration, which means it stays alive until you explicitly delete it. You should only use dynamic storage duration when you need it.
That is, you should always prefer creating objects with automatic storage duration when you can.

The main two situations in which you might require dynamic allocation:

  1. You need the object to outlive the current scope - that specific object at that specific memory location, not a copy of it. If you're okay with copying/moving the object (most of the time you should be), you should prefer an automatic object.
  2. You need to allocate a lot of memory, which may easily fill up the stack. It would be nice if we didn't have to concern ourselves with this (most of the time you shouldn't have to), as it's really outside the purview of C++, but unfortunately, we have to deal with the reality of the systems we're developing for.

When you do absolutely require dynamic allocation, you should encapsulate it in a smart pointer or some other type that performs RAII (like the standard containers). Smart pointers provide ownership semantics of dynamically allocated objects. Take a look at std::unique_ptr and std::shared_ptr, for example. If you use them appropriately, you can almost entirely avoid performing your own memory management (see the Rule of Zero).

Pointers

However, there are other more general uses for raw pointers beyond dynamic allocation, but most have alternatives that you should prefer. As before, always prefer the alternatives unless you really need pointers.

  1. You need reference semantics. Sometimes you want to pass an object using a pointer (regardless of how it was allocated) because you want the function to which you're passing it to have access that that specific object (not a copy of it). However, in most situations, you should prefer reference types to pointers, because this is specifically what they're designed for. Note this is not necessarily about extending the lifetime of the object beyond the current scope, as in situation 1 above. As before, if you're okay with passing a copy of the object, you don't need reference semantics.

  2. You need polymorphism. You can only call functions polymorphically (that is, according to the dynamic type of an object) through a pointer or reference to the object. If that's the behavior you need, then you need to use pointers or references. Again, references should be preferred.

  3. You want to represent that an object is optional by allowing a nullptr to be passed when the object is being omitted. If it's an argument, you should prefer to use default arguments or function overloads. Otherwise, you should preferably use a type that encapsulates this behavior, such as std::optional (introduced in C++17 - with earlier C++ standards, use boost::optional).

  4. You want to decouple compilation units to improve compilation time. The useful property of a pointer is that you only require a forward declaration of the pointed-to type (to actually use the object, you'll need a definition). This allows you to decouple parts of your compilation process, which may significantly improve compilation time. See the Pimpl idiom.

  5. You need to interface with a C library or a C-style library. At this point, you're forced to use raw pointers. The best thing you can do is make sure you only let your raw pointers loose at the last possible moment. You can get a raw pointer from a smart pointer, for example, by using its get member function. If a library performs some allocation for you which it expects you to deallocate via a handle, you can often wrap the handle up in a smart pointer with a custom deleter that will deallocate the object appropriately.

Calling C++ member functions via a function pointer

Read this for detail :

// 1 define a function pointer and initialize to NULL

int (TMyClass::*pt2ConstMember)(float, char, char) const = NULL;

// C++

class TMyClass
{
public:
int DoIt(float a, char b, char c){ cout << "TMyClass::DoIt"<< endl; return a+b+c;};
int DoMore(float a, char b, char c) const
{ cout << "TMyClass::DoMore" << endl; return a-b+c; };

/* more of TMyClass */
};
pt2ConstMember = &TMyClass::DoIt; // note: <pt2Member> may also legally point to &DoMore

// Calling Function using Function Pointer

(*this.*pt2ConstMember)(12, 'a', 'b');


Related Topics



Leave a reply



Submit