13-7 结构体、成员和成员选择介绍

在编程中,我们经常需要使用多个变量来表示感兴趣的内容。正如我们在上一章(12.1——复合数据类型简介)的引言中所讨论的,分数由分子和分母组成,它们相互关联,构成一个单一的数学对象。

或者,假设我们要编写一个程序,需要存储公司员工的信息。我们可能需要跟踪员工的姓名、职称、年龄、员工编号、经理编号、工资、生日、入职日期等属性……

如果我们使用自变量来追踪所有这些信息,结果可能如下所示:

std::string name;
std::string title;
int age;
int id;
int managerId;
double wage;
int birthdayYear;
int birthdayMonth;
int birthdayDay;
int hireYear;
int hireMonth;
int hireDay;

然而,这种方法存在一些问题。首先,这些变量之间是否真的相关并不明确(需要阅读注释或查看上下文才能确定)。其次,现在需要管理 12 个变量。如果我们想将这个员工传递给一个函数,就必须传递 12 个参数(并且顺序要正确),这将使我们的函数原型和函数调用变得一团糟。而且,由于一个函数只能返回一个值,那么函数又该如何返回一个员工呢?

如果我们想要记录多个员工的信息,就需要为每个额外的员工定义 12 个变量(每个变量都需要一个唯一的名称)!这显然无法扩展。我们真正需要的是一种将所有这些相关数据整理在一起的方法,以便更轻松地管理它们。

幸运的是,C++ 提供了两种复合类型来解决这类挑战:结构体(我们现在就来介绍)和类(稍后会详细介绍)。结构体struct,全称structure)是一种程序定义的数据类型(参见 13.1 节——程序定义(用户定义)类型简介),它允许我们将多个变量捆绑到一个类型中。正如您很快就会看到的,这使得管理相关变量集变得更加简单!

提醒:
结构体是一种类类型(类和联合体也是如此)。因此,适用于类类型的任何规则也适用于结构体。


定义结构体

由于结构体是程序自定义类型,因此在使用结构体之前,我们必须先告诉编译器结构体的类型定义。以下是一个简化的员工结构体定义示例:

struct Employee
{
    int id {};
    int age {};
    double wage {};
};

该关键字struct用于告诉编译器我们正在定义一个结构体,我们已经给它命名了Employee(因为程序定义的类型通常以大写字母开头命名)。

然后,在一对花括号内,我们定义每个 Employee 对象将包含的变量。在本例Employee中,我们创建的每个 Employee 对象都将包含 3 个变量:int idint agedouble wage。结构体中的变量称为数据成员data members (或成员变量member variables)。

提示
在日常用语中,“成员”指的是属于某个团体的个体。例如,你可能是篮球队的成员,而你的妹妹可能是合唱团的成员。
在 C++ 中,成员member是指属于结构体(或类)的变量、函数或类型。所有成员都必须在结构体(或类)的定义中声明。
我们将在以后的课程中经常用到“成员”这个词,所以一定要记住它的意思。

就像我们使用空花括号来初始化普通变量的值一样(1.4 - 变量赋值和初始化),每个成员变量后面的空花括号确保了Employee在创建对象时,对象内部的成员变量都被初始化Employee。我们将在几节课讲解默认成员初始化时详细讨论这一点(13.9 - 默认成员初始化)。

最后,我们以分号结束类型定义。

需要提醒的是,Employee这只是一个类型定义——此时实际上并没有创建任何对象。


定义结构体对象

为了使用该Employee类型,我们只需定义一个类型为 Employee的变量:

Employee joe {}; // Employee is the type, joe is the variable name

Employee定义了一个名为joe 的变量。代码执行时,会实例化一个包含三个数据成员的 Employee 对象。空的大括号确保对象是值初始化的。

与其他任何类型一样,可以定义多个相同结构体类型的变量:

Employee joe {}; // create an Employee struct for Joe
Employee frank {}; // create an Employee struct for Frank

访问成员

请看以下示例:

struct Employee
{
    int id {};
    int age {};
    double wage {};
};

int main()
{
    Employee joe {};

    return 0;
}

在上面的例子中,名称joe指的是整个结构体对象(包含成员变量)。要访问特定的成员变量,我们需要在结构体变量名和成员名之间使用成员选择运算符member selection operator(operator.)。例如,要访问 Joe 的年龄成员,我们可以这样使用joe.age。

结构体成员变量的工作方式与普通变量完全相同,因此可以对它们执行常规操作,包括赋值、算术运算、比较等。

#include <iostream>

struct Employee
{
    int id {};
    int age {};
    double wage {};
};

int main()
{
    Employee joe {};

    joe.age = 32;  // use member selection operator (.) to select the age member of variable joe

    std::cout << joe.age << '\n'; // print joe's age

    return 0;
}

打印出来的内容:

image

结构体的最大优势之一是,每个结构体变量只需要创建一个新名称(成员名称在结构体类型定义中就已经固定了)。在下面的示例中,我们实例化了两个Employee对象:joefrank

#include <iostream>

struct Employee
{
    int id {};
    int age {};
    double wage {};
};

int main()
{
    Employee joe {};
    joe.id = 14;
    joe.age = 32;
    joe.wage = 60000.0;

    Employee frank {};
    frank.id = 15;
    frank.age = 28;
    frank.wage = 45000.0;

    int totalAge { joe.age + frank.age };
    std::cout << "Joe and Frank have lived " << totalAge << " total years\n";

    if (joe.wage > frank.wage)
        std::cout << "Joe makes more than Frank\n";
    else if (joe.wage < frank.wage)
        std::cout << "Joe makes less than Frank\n";
    else
        std::cout << "Joe and Frank make the same amount\n";

    // Frank got a promotion
    frank.wage += 5000.0;

    // Today is Joe's birthday
    ++joe.age; // use pre-increment to increment Joe's age by 1

    return 0;
}

image

在上面的例子中,很容易区分哪些成员变量属于 Joe,哪些属于 Frank。这比使用单个变量提供了更高的组织层次。此外,由于 Joe 和 Frank 的成员变量名称相同,因此当存在多个相同结构体类型的变量时,也能确保一致性。

在下一课中,我们将继续探索结构体,包括如何初始化它们。

posted @ 2025-12-22 14:56  游翔  阅读(14)  评论(0)    收藏  举报