C++ 之结构体与共用体

参考

项目描述
搜索引擎Bing
C++ Primer Plus(第六版)史蒂芬·普拉达
精通C++ (第九版)托尼·加迪斯、朱迪·沃尔特斯、戈德弗雷·穆甘达 (著) / 黄刚 等 (译)

描述

项目描述
g++8.1.0
操作系统Windows 10 专业版(64 位)

结构体

在 C++ 中,能够同时存储多个元素的复合类型除了数组外还有结构体。两者的区别在于:

  1. 一个数组中的每一个元素都必须属于同一种数据类型。你可以创建一个整型数组,但其中不能出现除整型数据类型外的其他数据类型,比如单精度浮点型(float)及字符型(char)。

  2. 一个结构体中可以同时存在多个元素,且这些元素的数据类型没有限制。

命名

结构的命名与类相似,通常采取大驼峰式命名法,即帕斯卡命名法来为结构进行命名。而通过结构创建的结构体实例则采用小驼峰命名法来为其命名。

使用(基本)

声明

声明一个结构体时你需要使用到 struct 关键字。

举个栗子

// 导入 iostream 以使 C++ 程序具有基本的输入输出功能
#include <iostream>
// 导入 string 以使用 String 类创建字符串
// 使用 String 类创建字符串将获得更多的支持
#include <string>
// 使用标准名称空间 std
using namespace std;


int main() {
    // 声明一个结构体
    struct Mark{
        // 在结构体中声明一个用于存放字符串的变量
        string name;
        // 在结构体中声明一个用于存放整型数据的变量
        int age;
    };  // 注意这个分号
    // 使用 system() 函数以使得窗口暂停
    system("pause");
}

注:

  1. 使用 struct 关键字创建了一个结构体后,你需要使用一个名称去对其进行标记。在本示例中我们将声明的结构体标记为 Mark

  2. 在结构体中声明的变量可以使用所有的 C++ 所支持的类型。

  3. 在声明结构体后,请不要忘记末尾的分号。将该分号省略,C++ 将会抛出错误。

初始化

先创建后初始化

举个栗子

#include <iostream>
#include <string>
using namespace std;


int main() {
    // 声明结构体
    struct Info{
        string name;
        int age;
    };

    // 创建结构体
    Info info;

    // 此时 info 中的相关数据尚未被初识化,
    // 故从 info 中取出的数据不能确定。
    cout << info.name << endl;
    cout << info.age << endl;

    // 初始化结构体
    info.name = "RedHeart";
    info.age = 18;
    cout << "-------------***---------------" << endl;
    cout << info.name << endl;
    cout << info.age << endl;
    system("pause");
}

执行效果


8
-------------***---------------
RedHeart
18
请按任意键继续. . .
C++ 11 列表初始化
#include <iostream>
#include <string>
using namespace std;


int main() {
    struct Info{
        string name;
        int age;
    };

    // 通过初始化列表对结构体进行初始化操作
    Info info = {
        "RedHeart",
        18
    };

    // 对结构体中的数据进行访问
    cout << info.name << endl;
    cout << info.age << endl;
    
    system("pause");
}

执行效果

RedHeart
18
请按任意键继续. . .

注:

  1. 使用列表初始化方式对结构体进行初始化时不能发生同类数据类型之间的缩窄转换(范围,精度),即不能将(仅列出部分):

    • 浮点型字面量(也可以称之为常量,即直接在程序中写出的数据)提供给整型变量。
    • 整型常量提供给字符型变量。
    • 双精度浮点型字面量提供给单精度浮点型常量。
  2. 使用列表初始化对结构体进行初始化但未对结构体中的变量指定值,则该变量将被初始化为 0 。例如:

#include <iostream>
#include <string>
using namespace std;


int main() {
    struct Info{
        string name;
        int age;
    };

    // 使用列表初始化为结构体进行初始化操作,但未
    // 为 age 变量指定值,这将导致该变量被初始化为零。
    Info info = {
        "RedHeart"
    };

    Info test = {};

    cout << info.name << endl;
    cout << info.age << endl;

    system("pause");
}

执行结果

RedHeart
0
请按任意键继续. . .
  1. 使用列表初识化对结构体进行初始化操作时,关键字 struct= 均可以省略。

    • 完整形式
    // 声明一个结构体
    struct Info{};
    
    // 对结构体进行初始化
    struct Info info = {};
    
    • 简写形式
    // 声明一个结构体
    struct Info{};
    
    // 对结构体进行初始化
    // struct 关键字仅能在初始化过程中被省略    
    Info info {};
    
通过结构的构造函数对结构进行初始化

与 C++ 中的类相似,结构也含有构造函数,构造函数在使用结构类型创建结构时将被自动调用以创建结构。在你没有为结构指定构造函数时,结构将使用 C++ 提供的默认的构造函数来完成结构的实例的创建。

在 C++ 中,结构常用于在结构体实例创建时初始化结构体中的成员以提供结构成员的默认值。对此,请参考如下示例:

#include <iostream>
#include <string> 
using namespace std;


int main(){
    struct Notebook 
    {
        string title;
        string content;
        // 使用结构的构造函数对该结构进行初始化操作
        // 结构的构造函数的名称需要与其所属的结构类型的名称
        // 相同,这样 C++ 才能判断出结构中的函数,哪一个是
        // 该结构的构造函数。
        Notebook(){
            title = "First";
            content = "Hello World";          
        }
    };

    // 使用 Notebook 创建一个结构体,但并不对该结构体
    // 进行初始化操作。
    Notebook notebook;
    // 由于使用了结构的构造函数对结构进行了初始化操作,
    // 因此结构中的成员显示了期望的值。
    cout << notebook.title << endl;
    cout << notebook.content << endl;
    system("pause");
}

执行结果

First
Hello World
请按任意键继续. . .

注:

C++ 的结构中的构造函数与类中的构造函数存在许多相似的地方,例如:

  1. 你可以为构造函数提供参数,这意味着构造函数能够被重载(通过调用函数时使用的实参列表来判断调用同名函数中的哪一个函数)。
  2. C++ 不允许你为构造函数指明返回值类型,也不允许你使用构造函数返回某一数据。否则,C++ 将抛出错误。
  3. 你可以为 C++ 中的结构提供一个析构函数(析构函数的函数名称与构造函数的名称类似,你只需要在合法的构造函数名称前添加波浪号 ~ 即可定义一个析构函数),在结构被销毁时,C++ 将自动调用结构的析构函数以完成结构的销毁工作。需要注意的是,析构函数不能带有参数,这意味着析构函数无法被重载。同时,析构函数也不可使用返回值,也不可为其指定返回值类型。

使用(进阶)

结构数组

你可以创建一个元素为结构体的数组,方法和创建基本类型数组类似。

举个栗子

#include <iostream>
#include <string>
using namespace std;


int main() {
    struct Arr{
        string name;
        int age;
    };

    // 创建长度为 2 的结构数组
    Arr arr[2] {
        {"RedHeart", 18},
        {"TwoMoons"}
    };

    // 对结构数组中的元素进行访问
    cout << arr[0].name << '\t' << arr[0].age << endl;
    cout << arr[1].name << '\t' << arr[1].age << endl;
    system("pause");
}

执行结果

RedHeart        18
TwoMoons        0
请按任意键继续. . .

声明(拓展)

在前面,我们都是先对结构体进行声明后对其实现创建及初始化的。但其实,结构体的声明与结构体的创建及结构体的初始化是可以同时进行的。

声明及创建
#include <iostream>
#include <string>
using namespace std;


int main() {
    // 在对 Info 结构体进行声明时同时创建 info 及 test 结构体
    struct Info{
        string name;
        int age;
    }info, test;

    // 对未初始化的结构体进行访问
    cout << info.name << '\t' << info.age << endl;
    cout << test.name << '\t' << test.age << endl;
    system("pause");
}

执行结果

        8
        4200288
请按任意键继续. . .
声明及初始化
#include <iostream>
#include <string>
using namespace std;


int main() {
    // 在对 Info 结构体进行声明时同时初始化 info 及 test 结构体
    struct Info{
        string name;
        int age;
    }info{
        "RedHeart",
        18
    }, test{
        "TwoMoons"
    };

    // 对未初始化的结构体进行访问
    cout << info.name << '\t' << info.age << endl;
    cout << test.name << '\t' << test.age << endl;
    system("pause");
}

执行结果

RedHeart        18
TwoMoons        0
请按任意键继续. . .
匿名结构体

你可以不用为定义的结构体类型赋予标记,但这将导致你在使用该结构类型创建结构体后无法再次使用该结构类型创建结构体。

举个栗子

#include <iostream>
#include <string>
using namespace std;


int main() {
    // 声明并初始化结构体
    struct{
        string name;
        int age;
    }info{
        "RedHeart",
        18
    },test{
        "TwoMoons"
    };

    // 访问结构体中的变量
    cout << info.name << '\t' << info.age << endl;
    cout << test.name << '\t' << test.age << endl;

    system("pause");
}

执行结果

RedHeart        18
TwoMoons        0
请按任意键继续. . .

细节

外部声明与内部声明

在声明结构体时,你可以选择使用内部声明(即在函数内部对结构体进行声明)的方式对结构体进行声明,也可以选择使用外部声明(即在函数外部对结构体进行声明)的方式对结构体进行声明。

外部声明与内部声明的区别有:

  1. 通过外部声明的方式声明的结构体可以被多个函数所共享。在使用外部声明的方式声明结构体时,推荐将声明结构体的语句放置于所有函数之前,这样可以使得 C++ 中的所有函数都可以直接使用该结构体。

举个栗子

#include <iostream>
#include <string>
using namespace std;

struct GlobalStruct{
    string name;
    int age;
};

void func() {
    // 使用这部分语句将对 GlobalStruct 进行初始化
    GlobalStruct globalStruct{
        "RedHeart",
        19
    };

    // 访问结构体中的数据
    cout << "func()\t" << globalStruct.name << '\t' << globalStruct.age << endl;
}

int main() {
    // 对 func() 函数进行调用
    func();

    // 使用这部分语句将对 GlobalStruct 进行初始化
    GlobalStruct globalStruct{
        "RedHeart",
        19
    };

    // 访问结构体中的数据
    cout << "main()\t" << globalStruct.name << '\t' << globalStruct.age << endl;
    system("pause");
}

执行结果

func()  RedHeart        19
main()  RedHeart        19
请按任意键继续. . .

注:

请不要在与结构体的外部声明的相关语句之前使用结构体,否者,C++ 将抛出错误。例如:

#include <iostream>
#include <string>
using namespace std;


void func() {
    GlobalStruct globalStruct{
        "RedHeart",
        19
    };

    cout << "func()\t" << globalStruct.name << '\t' << globalStruct.age << endl;
}


// 将结构体的外部声明语句移至此处
struct GlobalStruct{
    string name;
    int age;
};


int main() {
    func();

    GlobalStruct globalStruct{
        "RedHeart",
        19
    };

    cout << "main()\t" << globalStruct.name << '\t' << globalStruct.age << endl;
    system("pause");
}
  1. 通过内部声明的方式声明的结构体仅能够被声明该结构体的函数所使用。

成员赋值

对于结构体来说,成员赋值即将属于同一结构类型(不同结构类型,C++ 将抛出错误)的两个结构体中的某一结构体的已初始化数据赋值给另一结构体。

举个栗子

#include <iostream>
#include <string>
using namespace std;


int main() {
    struct Info{
        string name;
        int age;
    };

    Info info{
        "RedHeart",
        18
    };

    Info test;

    // test 结构体中的原数据(未初始化)
    cout << test.name << '\t' << test.age << endl;
    // 将 info 结构体中已初始化的变量赋值给 test 结构体中的相应变量
    test = info;
    // test 结构体中的数据已被初始化
    cout << test.name << '\t' << test.age << endl;
    
    system("pause");
}

执行结果

        4200496
RedHeart        18
请按任意键继续. . .

注:

会被成员赋值操作所影响的变量仅为右值(可以简单理解为位于赋值语句中等号右边的内容)中已初始化的变量,未被初识化的变量不会参与到成员赋值过程。

举个栗子

#include <iostream>
#include <string>
using namespace std;


int main() {
    struct Info{
        string name;
        int age;
    };

    // 创建结构体 info,此处不适用列表初始化方式
    // 初始化结构体的原因是,列表初始化方式默认将未
    // 被人为初始化的变量初识化为 0 
    Info info;
    
    // 为 info 结构体初始化其变量 name
    info.name = "RedHeart";

    // 创建结构体 test
    Info test;

    // test 结构体中的原数据(未初始化)
    cout << test.name << '\t' << test.age << endl;
    // 将 info 结构体中已初始化的变量赋值给 test 结构体中的相应变量
    test = info;
    // test 结构体中的数据已被初始化
    cout << test.name << '\t' << test.age << endl;
    
    system("pause");
}

执行结果

        4200400
RedHeart        8
请按任意键继续. . .

成员函数

结构体中通常仅包含成员属性,但这并不意味着结构体中不可包含成员函数。对此,请参考如下示例:

#include <iostream>
#include <string> 
using namespace std;


int main(){
    struct Notebook 
    {
        string content = "Default";
        // 在结构中定义一个函数 write,该函数
        // 用于动态修改结构的成员变量 content。
        void write(string target){
           content = target;
        }
    };

    Notebook notebook;
    cout << notebook.content << endl;

    // 使用结构的成员函数 write() 动态修改成员变量 content
    notebook.write("Replace");
    cout << notebook.content << endl;
    system("pause");
}

执行结果

Default
Replace
请按任意键继续. . .

访问修饰符

与 C++ 中的类相似,结构中也允许使用访问修饰符。与类不同的是,类中默认使用的访问修饰符为 private ,而结构中默认使用的访问修饰符默认为 publicpublic 访问修饰符不对成员访问者做出限制。因此,在一般情况下,无需(结构通常用于存放数据)更改结构指明使用的访问修饰符。

共用体

共用体能够存储多种数据类型,但在某一时间仅能存储其中的一种数据类型。共用体的使用语法与结构体相似,在学习了结构体后相信你可以很快上手共用体。

举个栗子

#include <iostream>
using namespace std;


int main() {
    // 声明共用体
    union Content{
        int integer;
        char character;
    };  // 注意此处的分号,若缺失,C++ 将抛出错误。
    
    // 创建共用体
    union Content content;

    // 使用共用体存储字符串
    content.character = 'R';
    cout << content.character << endl;

    // 使用共用体存储整型数据
    content.integer = 100;
    cout << content.integer << endl;

    system("pause");
}

执行结果

R
100
请按任意键继续. . .

注:

  1. 创建共用体时,union 关键字可以省略。
  2. 在共用体使用另一种数据类型存储时,原数据类型将恢复至未初始化状态。

举个栗子

#include <iostream>
using namespace std;


int main() {
    union Content{
        int integer;
        char character;
    };
    
    union Content content;

    content.character = 'R';
    cout << content.character << endl;

    content.integer = 100;
    cout << content.integer << endl;

    // 使用 integer 存储数据后,向执行窗口输出
    // character 保存的内容。
    cout << content.character << endl;

    system("pause");
}

执行结果

R
100
d
请按任意键继续. . .

存储空间

共用体的用途之一是,当数据项使用两种或更多种格式(数据类型),但不会同时使用时,可有效的减少所需要的存储空间。

共用体的长度仅为其存储的多种数据类型中,占用内存空间最大的那一数据类型的长度。

举个栗子

#include <iostream>
using namespace std;


int main() {
    union Content{
        int integer;
        char character;
    };
    
    union Content content;

    // 由于 Content 共用体中占用内存空间最多的
    // 数据类型为 int,故该语句将打印数据类型
    // int 所占用的内存空间(字节),即 4
    cout << sizeof(Content) << endl;

    system("pause");
}

执行结果

4
请按任意键继续. . .

匿名共用体

匿名共用体即没有名称的共用体,你可以像匿名结构体那般在声明时同时创建一个匿名共用体。

举个栗子

#include <iostream>
using namespace std;


int main() {
    // 声明并创建一个匿名共用体
    union{
        int age;
        char symbol;
    }content;

    content.age = 18;
    cout << content.age << endl;

    content.symbol = 'R';
    cout << content.symbol << endl;

    system("pause");
}

执行结果

18
R
请按任意键继续. . .
同化

你若在一个结构体中声明一个匿名共用体,那么这个匿名共用体中的变量将被同化为匿名共用体所在的结构体中的变量。

举个栗子

#include <iostream>
using namespace std;


int main() {
    struct{
        union {
            int age;
            char symbol;
        };
    }info;

    info.age = 18;
    cout << info.age << endl;

    info.symbol = 'R';
    cout << info.symbol << endl;

    system("pause");
}

执行效果

18
R
请按任意键继续. . .

尾声

💕欢迎建议💕 如果你对这篇博客右什么意见,欢迎提出。

💞欢迎提问💞 如果各位对文章中的某些内容不太理解,欢迎提问。

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BinaryMoon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值