如何理解这个声明中的typedef

How to understand the typedef in this declaration

最近看了Effective C++这本书,第35条里面关于typedef的声明让我很困惑。

class GameCharacter; // Question1: Why use forward declaration?

int defaultHealthCalc(const GameCharacter& gc);

class GameCharacter{

    public:
        typedef int (*HealthCalcFunc)(const GameCharacter&); // Question 2: What does this mean?

        explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
            : healthFunc(hcf)
        {}

        int healthValue() const
        {return healthFunc(*this); }

    private:
        HealthCalcFunc healthFunc;
};

所以我的第一个问题是:为什么作者在这里使用前向声明?有什么具体原因吗?

我的第二个问题是:如何理解 typedef 声明,以及如何使用它?我只知道 typedef int MyInt;

typedef int (*HealthCalcFunc)(const GameCharacter&);

为函数指针类型声明一个名为HealthCalcFunctypedef,其中函数签名returns 为int 并采用const GameCharacter& 的参数。

需要前向声明,因为 class 在 typedef 中用作参数类型。

前向声明是因为defaultHealthCalc的前向声明,它使用了GameCharacter。如果没有前向声明,编译器就不知道后面会有一个名为GameCharacter的类型,所以你需要前向声明它,或者把定义移到函数声明之前。

typedef的意思是命名类型int(*)(const GameCharacter&)HealthCalcFunc。那是一个指向 int(const GameCharacter&) 的函数指针。因此,typedef 很难读,建议改用 using 声明:

using HealthCalcFunc = int(*)(const GameCharacter&);

So my first question is that why the author use forward declaration here?

因为函数defaultHealthCalc的声明使用了const GameCharacter&作为参数的类型,所以需要提前声明GameCharacter

And my second question is how to understand the typedef declaration

它声明了一个类型名HealthCalcFunc,它是一种指向函数的指针类型,它以const GameCharacter&为参数,returns int.

and how to use it?

正如代码示例所示,

explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) // declare a parameter with type HealthCalcFunc; 
                                                               // the default argument is function pointer to defaultHealthCalc
: healthFunc(hcf)                                              // initialize healthFunc with hcf
{}

int healthValue() const
{return healthFunc(*this); }                                   // invoke the function pointed by healthFunc

HealthCalcFunc healthFunc;                                     // declare a member with type HealthCalcFunc 

So my first question is that why the author use forward declaration here?

以便编译器在遇到 int defaultHealthCalc(const GameCharacter& gc); 行时知道 GameCharacter 是一个有效名称。

And my second question is how to understand the typedef declaration and how to use it?

理想情况下,您不再使用它。

从 C++11 开始,using 是更好的选择,因为它更具可读性;与 typedef 编辑的函数指针不同,它清楚地将名称与名称描述的内容分开。比较一下:

typedef int (*HealthCalcFunc)(const GameCharacter&);

有了这个:

using HealthCalcFunc = int (*)(const GameCharacter&);

typedef 版本中,名称 HealthCalcFunc 两边都被名称描述的内容包围。这会影响可读性。


但是代码仍然可以改进,因为 C++11 还引入了 std::function 作为 and/or 函数指针之上的抽象层的替代。

using HealthCalcFunc = std::function<int(const GameCharacter&)>;

这是非常可读的,几乎不需要解释。 HealthCalcFunc 是一个 returns 一个 int 并接受一个 const GameCharacter&.

的函数

defaultHealthCalc 函数符合此定义。这是一个完整的例子:

#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;
};

std::function 的优点在于您不受限于独立的函数。你可以传递每一个类似函数的东西。以下是一些示例:

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));
}

另见 Should I use std::function or a function pointer in C++?

GameCharacter 的前向声明不是严格需要的;函数声明可以是:

int defaultHealthCalc(const class GameCharacter& gc);

与原代码效果相同。但是将前向声明单独放在一行被认为更具可读性。