Virtual Methods or Function Pointers

doron picture doron · Dec 23, 2009 · Viewed 9.7k times · Source

When implementing polymorphic behavior in C++ one can either use a pure virtual method or one can use function pointers (or functors). For example an asynchronous callback can be implemented by:

Approach 1

class Callback
{
public:
    Callback();
    ~Callback();
    void go();
protected:
    virtual void doGo() = 0;  
};

//Constructor and Destructor

void Callback::go()
{
   doGo();
}

So to use the callback here, you would need to override the doGo() method to call whatever function you want

Approach 2

typedef void (CallbackFunction*)(void*)

class Callback
{
public:
    Callback(CallbackFunction* func, void* param);
    ~Callback();
    void go();
private:
   CallbackFunction* iFunc;
   void* iParam;
};

Callback::Callback(CallbackFunction* func, void* param) :
    iFunc(func),
    iParam(param)
{}

//Destructor

void go()
{
    (*iFunc)(iParam);
}

To use the callback method here you will need to create a function pointer to be called by the Callback object.

Approach 3

[This was added to the question by me (Andreas); it wasn't written by the original poster]

template <typename T>
class Callback
{
public:
    Callback() {}
    ~Callback() {}
    void go() {
        T t; t();
    }
};

class CallbackTest
{
public:
    void operator()() { cout << "Test"; }
};

int main()
{
    Callback<CallbackTest> test;

    test.go();
}

What are the advantages and disadvantages of each implementation?

Answer

Adisak picture Adisak · Dec 23, 2009

Approach 1 (Virtual Function)

  • "+" The "correct way to do it in C++
  • "-" A new class must be created per callback
  • "-" Performance-wise an additional dereference through VF-Table compared to Function Pointer. Two indirect references compared to Functor solution.

Approach 2 (Class with Function Pointer)

  • "+" Can wrap a C-style function for C++ Callback Class
  • "+" Callback function can be changed after callback object is created
  • "-" Requires an indirect call. May be slower than functor method for callbacks that can be statically computed at compile-time.

Approach 3 (Class calling T functor)

  • "+" Possibly the fastest way to do it. No indirect call overhead and may be inlined completely.
  • "-" Requires an additional Functor class to be defined.
  • "-" Requires that callback is statically declared at compile-time.

FWIW, Function Pointers are not the same as Functors. Functors (in C++) are classes that are used to provide a function call which is typically operator().

Here is an example functor as well as a template function which utilizes a functor argument:

class TFunctor
{
public:
    void operator()(const char *charstring)
    {
        printf(charstring);
    }
};

template<class T> void CallFunctor(T& functor_arg,const char *charstring)
{
    functor_arg(charstring);
};

int main()
{
    TFunctor foo;
    CallFunctor(foo,"hello world\n");
}

From a performance perspective, Virtual functions and Function Pointers both result in an indirect function call (i.e. through a register) although virtual functions require an additional load of the VFTABLE pointer prior to loading the function pointer. Using Functors (with a non-virtual call) as a callback are the highest performing method to use a parameter to template functions because they can be inlined and even if not inlined, do not generate an indirect call.