So my first question is that why the author use forward declaration here?
So that the compiler knows that GameCharacter
is a valid name when the int defaultHealthCalc(const GameCharacter& gc);
line is encountered.
And my second question is how to understand the typedef declaration and how to use it?
Ideally, you do not use it anymore.
Starting with C++11, using
is a superior alternative because it's more readable; unlike typedef
'ed function pointers, it clearly separates the name from that which the name describes. Compare this:
typedef int (*HealthCalcFunc)(const GameCharacter&);
With this:
using HealthCalcFunc = int (*)(const GameCharacter&);
In the typedef
version, the name HealthCalcFunc
is surrounded on both sides by that which the name describes. This hurts readability.
But the code can still be improved, because C++11 also introduced std::function
as an alternative to and/or an abstraction layer above function pointers.
using HealthCalcFunc = std::function<int(const GameCharacter&)>;
This is so readable it hardly has to be explained at all. HealthCalcFunc
is a function which returns an int
and takes a const GameCharacter&
.
The defaultHealthCalc
function fits into this definition. Here's a full example:
#include <functional>
class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter {
public:
using HealthCalcFunc = std::function<int(const GameCharacter&)>;
explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
: healthFunc(hcf)
{}
int healthValue() const
{return healthFunc(*this); }
private:
HealthCalcFunc healthFunc;
};
The great thing about std::function
is that you are not restricted to free-standing functions. You can pass every function-like thing. Here are some examples:
struct Functor
{
int f(const GameCharacter& gc);
};
int main()
{
// normal function:
GameCharacter character1(defaultHealthCalc);
// lambda:
GameCharacter character2([](auto const& gc) { return 123; });
// using std::bind:
Functor functor;
using namespace std::placeholders;
GameCharacter character3(std::bind(&Functor::f, functor, _1));
}
See also Should I use std::function or a function pointer in C++?.
std::function
here is appropriate, but it is a fully different alternative, and for a simple example such as this one, it might be mis-leading to mention improvements when switching from a light weight function pointer type to the entirely different and, in comparison, "heavy" creature ofstd::function
. Both have their place (mostly, imo,std::function
should be preferred, but when working with older APIs ...), but are very different concepts, and whether to use one over the other should be weighted against the context. – Mayolamayon