5

最近,我阅读了《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;

4

5 回答 5

14

所以我的第一个问题是为什么作者在这里使用前向声明?

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

我的第二个问题是如何理解 typedef 声明

它声明了一个类型 name HealthCalcFunc,它是一种指向函数的指针类型,它const GameCharacter&作为参数并返回int

以及如何使用它?

正如代码示例所示,

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 
于 2017-03-12T09:00:20.187 回答
11

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

typedef意味着命名类型int(*)(const GameCharacter&) HealthCalcFunc。这是一个指向 a 的函数指针int(const GameCharacter&)。出于这个原因,typedef 非常不可读,建议使用using声明来代替:

using HealthCalcFunc = int(*)(const GameCharacter&);
于 2017-03-12T08:59:37.013 回答
9
typedef int (*HealthCalcFunc)(const GameCharacter&);

为函数指针类型声明一个typedef命名HealthCalcFunc,其中函数签名返回 aint并采用 的参数const GameCharacter&

需要前向声明,因为该类用作typedef.

于 2017-03-12T08:58:36.960 回答
9

所以我的第一个问题是为什么作者在这里使用前向声明?

这样编译器在遇到GameCharacter该行时就知道这是一个有效的名称int defaultHealthCalc(const GameCharacter& gc);

而我的第二个问题是如何理解 typedef 声明以及如何使用它?

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

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

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

有了这个:

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

在该typedef版本中,名称HealthCalcFunc的两侧都围绕着名称所描述的内容。这会损害可读性。


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

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

这太可读了,几乎不需要解释。HealthCalcFunc是一个返回 anint并接受 a的函数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));
}

另请参阅我应该在 C++ 中使用 std::function 还是函数指针?.

于 2017-03-12T10:53:35.517 回答
2

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

int defaultHealthCalc(const class GameCharacter& gc);

这与原始代码具有相同的效果。但是将前向声明放在自己的行上被认为更具可读性。

于 2017-03-13T00:53:49.383 回答