恭喜你!你又完成了一个章节的学习。你在本章中获得的关于结构体的知识将在我们学习C++最重要的主题——类时发挥重要作用!
快速回顾
程序定义类型(也称为用户定义类型)是我们可以为自己的程序创建的自定义类型。枚举类型和类类型(包括结构体、类和联合体)允许创建程序定义类型。程序定义类型必须在使用之前进行定义。程序定义类型的定义称为类型定义。类型定义不受单一定义规则的限制。
枚举(也称为枚举类型或枚举类)是一种复合数据类型,其中所有可能的值都被定义为符号常量(称为枚举器)。枚举是不同的类型,这意味着编译器可以将它们与其他类型区分开来(与类型别名不同)。
未限定作用域的枚举之所以得名,是因为它们将枚举器的名称放入与枚举定义本身相同的作用域中(与命名空间创建新的作用域区域不同)。未限定作用域的枚举还为其枚举器提供了一个命名的作用域区域。未限定作用域的枚举会隐式地转换为整数值。
限定作用域的枚举与未限定作用域的枚举类似,但不会隐式地转换为整数,并且枚举器仅被放入枚举的作用域区域中(而不是枚举被定义的作用域区域中)。
结构体(简称struct)是一种程序定义的数据类型,它允许我们将多个变量捆绑在一起形成一个单一类型。结构体(或类)中包含的变量称为数据成员(或成员变量)。要访问特定的成员变量,我们在结构体变量名和成员名之间使用成员选择运算符(运算符.)(对于普通结构体和结构体的引用),或者使用成员选择指针运算符(运算符->)(对于指向结构体的指针)。
在一般编程中,聚合数据类型(也称为聚合体)是任何可以包含多个数据成员的类型。在C++中,仅包含数据成员的数组和结构体是聚合体。
聚合体使用一种称为聚合初始化的初始化形式,它允许我们直接初始化聚合体的成员。为此,我们提供一个初始化器列表作为初始化器,它只是一个由逗号分隔的值列表。聚合初始化是一种逐成员的初始化方式,这意味着结构体中的每个成员都按照声明顺序进行初始化。
在C++20中,指定初始化器允许你明确指定哪些初始化值映射到哪些成员。成员必须按照它们在结构体中声明的顺序进行初始化,否则会导致错误。
当我们定义一个结构体(或类)类型时,我们可以在类型定义中为每个成员提供一个默认初始化值。这个过程称为非静态成员初始化,初始化值称为默认成员初始化器。
出于性能原因,编译器有时会在结构体中添加间隔(这称为填充),因此结构体的大小可能大于其成员大小的总和。
类模板是用于实例化类类型(结构体、类或联合体)的模板定义。类模板参数推导(CTAD)是C++17的一个特性,它允许编译器从初始化器中推导模板类型参数。
测验时间
好啦!
问题1
在设计一款游戏时,我们决定要加入怪物,因为大家都喜欢与怪物战斗。声明一个代表怪物的结构体。怪物的类型可以是以下几种之一:食人魔、龙、兽人、巨型蜘蛛或史莱姆。
每个单独的怪物还应该有一个名字(使用std::string),以及一个表示生命值的数值,该数值代表它们在死亡前可以承受的伤害量。编写一个名为printMonster()的函数,用于打印出结构体的所有成员。使用初始化器列表初始化一个食人魔和一个史莱姆,并将它们传递给printMonster()。
你的程序应该产生以下输出:
这个食人魔的名字是Torg,生命值为145。
这个史莱姆的名字是Blurp,生命值为23。
显示答案
问题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{};
// Player结构体仍在开发中。将添加更多成员。
};
显示答案
h) int(当null是有效参数时)
显示答案
i) std::string_view
显示答案
问题3
创建一个名为Triad的类模板,该模板有3个相同模板类型的成员。同时创建一个名为print的函数模板,用于打印Triad。以下程序应该可以编译:
int main()
{
Triad t1{ 1, 2, 3 }; // 注意:使用CTAD推导模板参数
print(t1);
Triad t2{ 1.2, 3.4, 5.6 }; // 注意:使用CTAD推导模板参数
print(t2);
return 0;
}
并产生以下结果:
[1, 2, 3][1.2, 3.4, 5.6]
如果你使用的是C++17,你需要为CTAD提供一个推导指南(参见13.14——类模板参数推导(CTAD)和推导指南,了解相关信息)。
显示答案