恭喜!你又完成了一章。你所获得的关于结构体的知识将在我们学习 C++ 最重要的主题——类时非常有用!
快速回顾
程序定义类型(也称为用户定义类型)是一种我们可以创建的自定义类型,用于我们自己的程序中。枚举类型和类类型(包括结构体、类和联合体)允许创建程序定义类型。程序定义类型必须在使用前定义。程序定义类型的定义称为类型定义。类型定义不受“单一定义规则”的约束。
枚举(也称为枚举类型或enum)是一种复合数据类型,其中每个可能的值都定义为符号常量(称为枚举器)。枚举是不同的类型,这意味着编译器可以区分它们与其他类型(与类型别名不同)。
无作用域枚举之所以这样命名,是因为它们将其枚举器名称放入与枚举定义本身相同的范围(而不是像命名空间那样创建新的作用域区域)。无作用域枚举也为其枚举器提供了一个命名作用域区域。无作用域枚举将隐式转换为整数值。
有作用域枚举的工作方式与无作用域枚举类似,但不会隐式转换为整数,并且枚举器仅放置在枚举的作用域区域中(而不是放置在定义枚举的作用域区域中)。
结构体(structure 的简称)是一种程序定义数据类型,它允许我们将多个变量捆绑到一个单一类型中。结构体(或类)的一部分的变量称为数据成员(或成员变量)。要访问特定的成员变量,我们使用成员选择运算符(operator.
)在结构体变量名和成员名之间(对于普通结构体和结构体引用),或者使用来自指针的成员选择运算符(operator->
)(对于结构体指针)。
在通用编程中,聚合数据类型(也称为聚合)是任何可以包含多个数据成员的类型。在 C++ 中,仅包含数据成员的数组和结构体是聚合。
聚合使用一种称为聚合初始化的初始化形式,它允许我们直接初始化聚合的成员。为此,我们提供一个初始化列表作为初始化器,它只是一个逗号分隔值的列表。聚合初始化执行成员初始化,这意味着结构体中的每个成员都按照声明的顺序进行初始化。
在 C++20 中,指定初始化器允许你明确定义哪些初始化值映射到哪些成员。成员必须按照它们在结构体中声明的顺序进行初始化,否则会出错。
当我们定义一个结构体(或类)类型时,我们可以为每个成员提供一个默认初始化值作为类型定义的一部分。这个过程称为非静态成员初始化,初始化值称为默认成员初始化器。
出于性能原因,编译器有时会在结构体中添加间隙(这称为填充),因此结构体的大小可能大于其成员大小的总和。
类模板是用于实例化类类型(结构体、类或联合体)的模板定义。类模板参数推导 (CTAD) 是 C++17 的一个特性,它允许编译器从初始化器中推导出模板类型参数。
小测验时间
耶!
问题 #1
在设计游戏时,我们决定要有怪物,因为每个人都喜欢打怪。声明一个表示你的怪物的结构体。怪物应该有一个类型,可以是以下之一:食人魔、龙、兽人、巨型蜘蛛或史莱姆。
每个独立的怪物还应该有一个名字(使用 std::string
),以及一个生命值,表示它们在死亡前可以承受多少伤害。编写一个名为 printMonster()
的函数,它打印出结构体的所有成员。实例化一个食人魔和一个史莱姆,使用初始化列表初始化它们,并将它们传递给 printMonster()
。
你的程序应该产生以下输出:
This Ogre is named Torg and has 145 health. This Slime is named Blurp and has 23 health.
问题 #2
指定以下给定类型的对象应该通过值、常量地址还是常量引用传递。你可以假设将这些类型作为参数的函数不会修改它们。
a) char
b) std::string
c) unsigned long
d) bool
e) 枚举类型
f)
struct Position
{
double x{};
double y{};
double z{};
};
g)
struct Player
{
int health{};
// The Player struct is still under development. More members will be added.
};
h) int
(当 null 是有效参数时)
i) std::string_view
问题 #3
创建一个名为 Triad
的类模板,它有 3 个相同模板类型的成员。再创建一个名为 print
的函数模板,可以打印一个 Triad。以下程序应该可以编译:
int main()
{
Triad t1{ 1, 2, 3 }; // note: uses CTAD to deduce template arguments
print(t1);
Triad t2{ 1.2, 3.4, 5.6 }; // note: uses CTAD to deduce template arguments
print(t2);
return 0;
}
并产生以下结果:
[1, 2, 3][1.2, 3.4, 5.6]
如果你使用 C++17,你需要提供一个推导指南才能使 CTAD 工作(有关信息,请参阅 13.14 -- 类模板参数推导 (CTAD) 和推导指南)。