最近,我读了 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;
答案 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));
}
答案 4 :(得分:2)
GameCharacter
的前瞻声明并非严格要求;函数声明可以读取:
int defaultHealthCalc(const class GameCharacter& gc);
与原始代码具有相同的效果。但是,在自己的路线上进行前瞻声明被认为更具可读性。