“class”在参数前的意思是什么?

5
Unreal Engine 生成以下函数:
void AFlyingPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
    //stuff...
}

注意参数类型前的"class"关键字,它是什么意思?

2
这与没有“类”是一样的。 - DeiDei
但是为什么应该把它放在那里?还有任何参考资料吗? - Shital Shah
2
在C语言中,如果你有一个名为struct foo的结构体,你需要这样使用它:struct foo f;。而在C++中,这并不是必须的,但为了向后兼容性,C++仍然保留了这种用法,并将其扩展到了class关键字上。 - DeiDei
@DeiDei,C语言没有类,所以我非常怀疑这是为了向后兼容而进行的。看起来前向声明是目前最有可能的理论。 - Shital Shah
3个回答

8

第一种可能性,如果UInputComponent没有被声明,那么这可能是前置声明。因此

void AFlyingPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)

forward声明了一个名为UInputComponent的类,而参数InputComponent的类型是UInputComponent*

Note that a new class name may also be introduced by an elaborated type specifier which appears as part of another declaration, but only if name lookup can't find a previously declared class with the same name.

class U;
namespace ns{
    class Y f(class T p); // declares function ns::f and declares ns::T and ns::Y
    class U f(); // U refers to ::U
    Y* p; T* q; // can use pointers and references to T and Y
}

第二个可能性是,关键字class可能用于消除歧义。

如果在作用域中存在与类类型名称相同的函数或变量,则可以在名称前加上class以消除歧义,从而得到一个详细类型说明符

例如:

int UInputComponent;
class UInputComponent { /* ... */ };

// without the class keyword it won't compile because the name UInputComponent is ambiguous
void AFlyingPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent) 

第三种可能性,它可能没有任何意义。

如果UInputComponent已被声明为类,则使用关键字class或不使用都不会改变任何内容。请注意,如果先前声明的类型不匹配,则编译将失败。


谢谢。那么它是哪一个呢?;) - Shital Shah
@ShitalShah 这取决于上下文,但我会赌第一个。 :) - songyuanyao

3

Unreal 引擎工作原理

可能你已经知道,Unreal 通过管理同一基础类的多个实现来定义公共基础。每个开发人员必须使用引擎提供的类创建子类,以便在引擎中执行任务。

在这种情况下,我们需要一个 InputComponent,用于处理用户输入,解释并传递给Controllers和/或 Pawns

例如,如果您要定义像 PawnsPlayerControllers, AIControllers, HUD 等元素,则可以在 GameMode 中进行配置,然后将其配置到项目设置或直接通过 World Settings 配置到关卡中(如果您的关卡需要特定的GameMode)。这些引用也是类,将由引擎在适当的时间内实例化以设置游戏。

这里是陷阱

有了这个想法,这里就有缺点了。在UE4 C++中,因为引擎连结了松散的端点,有时您无法使用某些类,因为它们没有声明。当然,您可以包括它们,但是请考虑:如果您为一个类需要包括所有必要的内容,只是发现另一个类可能间接地要求该类,那么将会产生多少循环依赖关系?

解决方案是前向声明。在这种情况下,有一种特殊的风格称为速记前向声明,其中您可以在 使用该类的确切位置声明类型

如果您只使用它一次,那么这非常方便,以免在文件开头出现可怕的声明列表。

将其带到现实世界中

例如,如果您想知道当前默认 Pawn 类的定义,可以在您的 GameMode 中检查 GetDefaultPawnClass 公共变量(我们称之为 MyGameMode)。变量定义如下:

TSubclassOf < APawn > DefaultPawnClass

看到那个TSubclassOf了吗?它实际上是一个类模板,用于确保类型安全。这是一个提示给编辑器,只显示从APawn派生的类。

如果您使用自定义类型,并且基于我迄今讨论的内容,您可以找到如下内容:

TSubclassOf<class ASpeedyPawn> MySpeedyPawn;

1
通常使用class关键字来声明一个新的类类型,但在这种情况下不是这样。这被称为“前向声明”。当你想要使用的类型未知时,可以使用它们。例如,想象一下两个头文件。它们各自使用另一个的类型。这是一个循环依赖关系,是不允许的。但你仍然需要在每个文件中使用其他类型,对吧?你可以在一个文件中进行前向声明,然后就可以摆脱包含文件了。摆脱包含文件解决了循环依赖问题,并使你失去了类型的所有信息(例如可用成员),但你仍然可以使用该类型本身。这也节省了很多“空间”,因为编译器不必包含其他头文件。我不想说太多,因为SO上已经有一个很好的答案:When can I use a forward declaration?
那篇文章没有很好地解释为什么要进行前向声明。正如我所说,它解决了循环依赖并避免了包含类型。包含文件意味着让编译器将文件内容复制到include的位置。但你并不总是需要知道文件的内容。如果你只使用链接中描述的功能就可以省略include。这反过来会减少编译器输出的大小。

我理解前向声明,但迄今为止我从未见过在参数中进行这样的声明。我查阅了C++语言参考手册中的参数部分,但并没有找到任何说明这是可能的内容。 - Shital Shah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接