如何理解此声明中的typedef

时间:2017-03-12 08:54:11

标签: c++ typedef forward-declaration effective-c++

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

这样的东西

5 个答案:

答案 0 :(得分:13)

  

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

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

  

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

它声明了一个类型名称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 

答案 1 :(得分:11)

由于使用defaultHealthCalc GameCharacter的前瞻性声明,因此需要转发声明。如果没有前向声明,编译器将不会知道稍后会有一个名为GameCharacter的类型,因此您需要转发声明它,或者在函数声明之前移动定义。

typedef表示将类型命名为int(*)(const GameCharacter&) HealthCalcFunc。这是指向int(const GameCharacter&)的函数指针。因此,typedef非常难以理解,建议使用using声明:

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

答案 2 :(得分:9)

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

为函数指针类型声明一个名为typedef的{​​{1}},其中函数签名返回HealthCalcFunc并且参数为int

需要前向声明,因为该类在const GameCharacter&中用作参数类型。

答案 3 :(得分:8)

  

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

因此,当遇到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是一个返回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++?

答案 4 :(得分:2)

GameCharacter的前瞻声明并非严格要求;函数声明可以读取:

int defaultHealthCalc(const class GameCharacter& gc);

与原始代码具有相同的效果。但是,在自己的路线上进行前瞻声明被认为更具可读性。