C++ struct 笔记(超级详细)

今日碎碎念:我在学C语言时经常用到结构体struct,之后在写C++程序时遇到在struct中定义构造函数和成员函数的情况,这在c语言中是从未遇到过的,觉得奇怪,想到之前并没有真正系统学习C++里的struct,有必要今天详细记录一下。

   虽然今天结构体较少使用,但知道它们是什么,以及如何使用它们仍然很重要,这并不仅仅是因为可以在较老的程序中遇到它们,还因为在某些情况(这里请看二、C++ class和struct的区别)下,类的实例无法使用,这时必须使用结构体。

目录

一、C++ struct的用法

    1、struct是什么

     2、 struct的4种声明定义方式

     3、结构体的2种初始化方式

     4、结构体成员的访问

     5、结构体的嵌套

     6、将结构体作为参数和返回值

二、C++ class和struct的区别

三、C++ struct和 c struct的区别 

四、struct计算结构体某个成员相对于结构体基址的偏移


一、C++ struct的用法

    1、struct是什么

        struct是程序员定义的数据类型,将逻辑上连接在一起的不同类型的数据组合到一起的单元。

     2、 struct的4种声明定义方式

           a. 第一种语法表示

        struct 结构体名称
        {
           数据类型 member1;
           数据类型 member2;
        }; 

#include<iostream>
#include <string>
using namespace std;
struct SStudent
{int nNo;std::string  strName;
};int main(int argc, char *argv[])
{//使用初始化列表初始化structstruct SStudent s1 = { 1, "ying"}; //C++定义struct变量时,前面的struct可以写SStudent s2 = { 2, "Ming"};  //C++定义struct变量时,前面的struct也可以不写cout << s1.nNo << endl;cout << s1.strName << endl;cout << s2.nNo << endl;cout << s2.strName << endl;return 0;
}

        b.第二种语法表示

        typedef struct 结构体名称{
           数据类型 member1;
           数据类型 member2;
        }结构体名称别名;
       

  这种情况使用typedef关键字,声明了数据类型的别名,所以在定义结构体 变量时有两种方式:
    第一种:结构体名称 构体变量名
    第二种:结构体名称别名 结构体变量名

#include<iostream>
#include <string>
using namespace std;typedef struct SStudent
{int nNo;std::string  strName;
} SStud;int main(int argc, char *argv[])
{SStudent s1 = { 1, "ying"}; //使用结构体名称SStud s2 = { 2, "Ming"};    //使用结构体名称别名cout << s1.nNo << endl;cout << s1.strName << endl;cout << s2.nNo << endl;cout << s2.strName << endl;return 0;
}

   c.第三种语法表示

        struct 结构体名称{
           数据类型 member1;
           数据类型 member2;
        }结构体变量;

#include<iostream>
#include <string>
using namespace std;
struct SStudent
{int nNo;std::string  strName;
} stu;int main(int argc, char *argv[])
{
//    stu s2; //错误,没有使用typedef关键字,stu是变量不是类型stu = { 1, "ying"}; //使用初始化表赋值stu.nNo = 2;        //修改成员nNo值stu.strName = "XiaoMing";//修改成员strName值cout << stu.nNo << endl;cout << stu.strName << endl;return 0;
}

   d.第四种语法表示

   //匿名结构体

        struct {
           数据类型 member1;
           数据类型 member2;
        }结构体变量名;

在声明的同时定义了结构体变量,但不能在其它地方声明,因为我们无法得知该结构体的标识符,所以就无法通过标识符来声明变量。

#include<iostream>
#include <string>
using namespace std;//声明了结构体并定义(且只能在这里定义)了两个该结构体的变量,
//由于无法获取结构体名称(因为匿名),所以无法在其他地方定义该结构体变量
struct
{int nNo;std::string  strName;
} stu1, stu2;int main(int argc, char *argv[])
{ stu1 = { 1, "ying"}; //使用初始化表赋值stu2 = { 2, "Ming"}; //使用初始化表赋值cout << stu1.nNo << endl;cout << stu1.strName << endl;cout << stu2.nNo << endl;cout << stu2.strName << endl;return 0;
}

3、结构体的2种初始化方式

   a. 使用初始化列表

     语法:

         //初始化列表中的项目是按照结构体声明中成员的顺序依次赋值,各个成员的赋值用逗号分隔并用大括号括起来

        结构体类型 变量名 = {member1取值,member2取值,member3取值...};

         前面的例子都使用了初始化列表来初始化,这里不再记录例子。

        几个注意点:

        1)使用初始化列表时可以仅仅初始化部分成员,如果某个成员未被初始化,则所有跟在它后面的成员都需要保留为未初始化。C++未提供跳过某个成员初始化其他成员的方法。

#include<iostream>
#include <string>
using namespace std;//声明了结构体,增加nAge成员,并且定义了两个变量stu1和stu2
struct SStudent
{int nNo;std::string  strName;int nAge;
} stu1, stu2;int main(int argc, char *argv[])
{ stu1 = { 1, "ying"}; //合法,仅初始化了nNo和strName,不初始化nAge
//    stu2 = { 2, 11}; //非法,不能跳过strName给nAge赋值cout << stu1.nNo << endl;cout << stu1.strName << endl;cout << stu1.nAge << endl;cout << stu2.nNo << endl;cout << stu2.strName << endl;cout << stu2.nAge << endl;return 0;
}

       2)可以在结构体声明中使用默认值初始化结构体成员。

#include<iostream>
#include <string>
using namespace std;//声明了结构体,并且定义了两个变量stu1和stu2
struct SStudent
{int nNo = 1;std::string  strName = "YunCai";int nAge = 6;
} stu1, stu2;int main(int argc, char *argv[])
{ stu1 = { 2, "ying"}; //不给nAge赋值,将取值默认值6cout << stu1.nNo << endl;cout << stu1.strName << endl;cout << stu1.nAge << endl;cout << stu2.nNo << endl;    //不给stu2赋值,所有成员都使用默认值cout << stu2.strName << endl;cout << stu2.nAge << endl;return 0;
}

   运行结果:

   b. 使用构造函数

        与类构造函数一样,结构体的构造函数必须是与结构体名称相同的公共成员函数,并且没有返回类型。因为默认情况下,所有结构体成员都是公开的,所以不需要使用关键字 public。

#include<iostream>
#include <string>
using namespace std;//声明了结构体,
//并且定义了一个带有三个默认参数的构造函数
//在定义SStudent 变量而不向其传递任何参数时,提供默认值
struct SStudent
{int m_nNo;string  m_strName;int m_nAge;SStudent(int nNo = 1, string strName = "YunCai", int nAge = 6){m_nNo = nNo;m_strName = strName;m_nAge = nAge;}
};int main(int argc, char *argv[])
{ SStudent stu1; //调用构造函数并且使用默认参数SStudent stu2(2, "ying"); //调用构造函数并传参nNo = 2, strName = "ying"cout << stu1.m_nNo << endl;cout << stu1.m_strName << endl;cout << stu1.m_nAge << endl;cout << stu2.m_nNo << endl;cout << stu2.m_strName << endl;cout << stu2.m_nAge << endl;return 0;
}

4、结构体成员的访问

结构体数据成员都是public的,所以它们可以被直接访问,并且可以像常规变量一样使用。

结构体变量使用点运算符.访问数据成员,结构体指针使用->指针运算符访问数据成员。

#include<iostream>
#include <string>
using namespace std;struct SStudent
{int m_nNo;string  m_strName;int m_nAge;SStudent(int nNo = 1, string strName = "YunCai", int nAge = 6){m_nNo = nNo;m_strName = strName;m_nAge = nAge;}
};int main(int argc, char *argv[])
{ SStudent stu1; //调用构造函数并且使用默认参数SStudent stu2(2, "ying"); //调用构造函数并传参nNo = 2, strName = "ying"SStudent* stu3 = new SStudent(3, "Ming", 8); //使用new初始化SStudent对象指针cout << stu1.m_nNo << endl;//使用点运算符访问cout << stu1.m_strName << endl;cout << stu1.m_nAge << endl;cout << stu2.m_nNo << endl;cout << stu2.m_strName << endl;cout << stu2.m_nAge << endl;cout << stu3->m_nNo << endl;//使用指针运算符访问cout << stu3->m_strName << endl;cout << stu3->m_nAge << endl;delete stu3;stu3 = nullptr;return 0;
}

5、结构体的嵌套

就像一个类的对象可以作为成员放在另一个类中,一个结构体的变量可以作为成员放在另一个结构体中。

#include<iostream>
#include <string>
using namespace std;//声明了结构体SDate日期
struct SDate
{int m_nYear;int m_nMonth;int m_nDay;SDate(const int& nY = 2000, const int& nM = 11, const int& nD = 2){m_nYear = nY;m_nMonth = nM;m_nDay = nD;}
};//声明了结构体SStudent学生,其中m_dateBirth是学生出生日期
struct SStudent
{int m_nNo;string  m_strName;int m_nAge;SDate m_dateBirth;SStudent(const int& nNo = 1, const string& strName = "YunCai",const int&  nAge = 6, const SDate& dateBirth= SDate()){m_nNo = nNo;m_strName = strName;m_nAge = nAge;m_dateBirth = dateBirth;}
};//使用一个函数打印学生信息,参数类型为常量引用SStudent
void print(const SStudent& stu)
{cout << stu.m_nNo << " " << stu.m_strName << "  " << stu.m_nAge << "  ";cout <<"Birthday:" << stu.m_dateBirth.m_nYear << "_" <<stu.m_dateBirth.m_nMonth << "_" << stu.m_dateBirth.m_nDay << endl;
}//修改学号增加100,参数类型为值传递,返回值为SStudent
SStudent UpdateNo1(SStudent stu)
{stu.m_nNo += 100;return stu;
}//修改学号增加100,参数类型为引用传递
void UpdateNo2(SStudent& stu)
{stu.m_nNo += 100;
}int main(int argc, char *argv[])
{ SStudent stu1; //调用构造函数并且使用默认参数print(stu1);stu1 =  UpdateNo1(stu1);print(stu1);SDate stu2_birthday(1999, 9, 10);SStudent stu2(2, "Ying", 7, stu2_birthday);//调用够用构造函数并且传了4个参数print(stu2);UpdateNo2(stu2);print(stu2);return 0;
}

运行结果:

6、将结构体作为参数和返回值

  a.将结构体作为参数:与类对象一样,结构体变量也可以通过值、引用和常量引用传递给函数。

        1)   值传递:需要生成整个原始结构的副本并传递给函数。因为不希望浪费时间来复制整个结构体,所以,除非结构很小,否则一般会通过引用将结构体传递给函数。

        2)引用传递:引用传递不会生成原始结构体的副本,函数可以直接访问原始结构体的成员变量,也可能更改它们。

        3)常量引用传递:常量引用传递也不会生成原始结构体的副本,函数直接访问原始结构体的成员变量但不能修改原始结构体的成员变量。

b.将结构体作为返回值:函数的返回类型是结构体的名称。

在5、结构体的嵌套中的例子,print函数使用了常量引用传递进行传值,UpdateNo1函数的参数类型为值传递,返回值为结构体,UpdateNo2函数的参数类型为引用传递。

二、C++ class和struct的区别

      结构体和类基本雷同,唯一区别是,类中成员变量默认为私有private,而结构体中则为公有public。因此在使用时,我们可以根据不同的场景或者需求来选择使用struct或者class。

         a.这些情况下使用struct比class更好

                1)纯数据结构:如果一个类只包含数据成员,而没有成员函数;

                 2)数据成员全部为public;

                3)用于C接口:如果一个类需要与C语言交互,例如作为C语言库的接口,那么使用struct更加合适(因为C语言不支持类的概念,而使用struct可以更加方便地进行数据传递);

                4)继承自C结构体:如果一个类需要继承自一个C语言的结构体,那么使用struct更加合适。因为C结构体默认为public,并且在C++中可以使用struct来继承。

        b.这些情况下使用class比struct更好

                1)需要保证数据的安全性,对数据的访问控制比较严格:使用class可以将数据成员设置为私有成员private,防止外部直接修改数据;

                2)需要进行多态:如果一个类需要进行多态操作,例如需要使用虚函数,那么使用class更加合适。

三、C++ struct和 c struct的区别 

        a. C++中定义结构体变量时可以省略struct,但C语言中不可以省略;

        b. C++中struct可以和类一样,有访问权限,并可以定义成员函数; C语言中struct没有访问权限的设置,是一些变量的集合体,不能定义成员函数;

        c.C++中struct可以继承,也可以实现多态;C语言中struct不支持继承和多态;

四、struct计算结构体某个成员相对于结构体基址的偏移

如何计算:

           #define offsetof(s,m) (size_t)&(((s *)0)->m)

解释:

         ((s *)0):强制转化成数据结构指针,并使其指向地址0;
        ((s *)0)->m:使该指针指向成员m
        &(((s *)0)->m):获取该成员m的地址
        (size_t)&(((s *)0)->m):转化这个地址为合适的类型

使用场景

        offsetof已经被定义在系统库中,所以直接使用offsetof计算结构体某个成员相对于结构体基址的偏移

例子:

        要计算如下SDate中m_nDay成员相对于结构体基址的偏移,可以使用 offsetof(SDate, m_nDay)。

        struct SDate{
                int m_nYear;
                int m_nMonth;
                int m_nDay;
        };



参考资料如下:

1、C++ struct的4种定义方式_c++ struct 定义_Mr顺的博客-CSDN博客

2、C++结构体完全攻略(超详细) 

3、什么时候以struct 替代class? - 知乎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/65381.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode - 360周赛

一&#xff0c;2833. 距离原点最远的点 这道题的意思是&#xff0c;遇到 "L" 向左走&#xff0c;遇到 "R" 向右走&#xff0c;遇到 "_" 左右都可以走&#xff0c;那么要想找到距离原点最远的点&#xff0c;就是在找 | "L" "R&qu…

纵行科技与山鹰绿能达成合作,提供物联网资产管理数据服务

近日&#xff0c;纵行科技与山鹰绿能宣布双方达成深度合作关系&#xff0c;纵行科技将为山鹰绿能提供专业的物联网技术服务&#xff0c;使用物联网技术帮助山鹰绿能对循环包装载具等资产进行在线管理和数字化运营。 据悉&#xff0c;山鹰绿能是一家由山鹰国际控股的全资子公司…

前端将UTC时间格式转化为本地时间格式~~uniapp写法

UTC时间格式是什么 首先我们先简单的了解一下&#xff1a;UTC时间&#xff08;协调世界时&#xff0c;Coordinated Universal Time&#xff09;使用24小时制&#xff0c;以小时、分钟、秒和毫秒来表示时间 HH:mm:ss.SSSHH 表示小时&#xff0c;取值范围为00到23。mm 表示分钟…

Compose学习 - 环境配置及compose、kotlin插件、gradle、AndroidStudio版本对应关系

最近学习Compose&#xff0c;一开始学习的Compose版本是1.1.1&#xff0c;学习的过程中发现&#xff0c; LazyHorizontalGrid这个方法只有在1.2.0以后版本才支持。 想着既然要升级&#xff0c;直接用最新的好了。后面按照官网建议&#xff0c;下载了最新的AndroidStudio&#…

知识图谱实战应用26-基于知识图谱构建《本草纲目》的中药查询与推荐项目应用

大家好,我是微学AI,今天给大家介绍一下知识图谱实战应用26-基于知识图谱构建《本草纲目》的中药查询与推荐项目应用,本文通过Py2neo连接到知识图谱数据库,系统实现了中药的快速查询、关系分析、智能推荐和知识展示等功能。用户可以输入中药的名称或特征进行查询,系统将从知…

在Windows10上编译grpc工程,得到protoc.exe和grpc_cpp_plugin.exe

grpc是google于2015年发布的一款跨进程、跨语言、开源的RPC(远程过程调用)技术。使用C/S模式&#xff0c;在客户端、服务端共享一个protobuf二进制数据。在点对点通信、微服务、跨语言通信等领域应用很广&#xff0c;下面介绍grpc在windows10上编译&#xff0c;这里以编译grpc …

MATLAB实现AHP层次分析法——以情人节选取礼物为例

问题背景&#xff1a; 情人节来临之际&#xff0c;广大直男&#xff08;女&#xff09;同胞在给异性朋友选购礼物时会遇到难题——什么才是礼物好坏最重要的标准&#xff1f;基于层次分析法AHP进行计算&#xff0c;得出最高权重的指标&#xff0c;给出各位朋友选购礼物的一种思…

PY32F003F18P单片机概述

PY32F003F18P单片机是普冉的一款ARM微控制器&#xff0c;内核是Cortex-M0。这个单片机的特色&#xff0c;就是价格便宜&#xff0c;FLASH和SRAM远远超过8位单片机&#xff0c;市场竞争力很强大。 一、硬件资源&#xff1a; 1)、FLASH为64K字节&#xff1b; 2)、SRAM为8K字节&…

无涯教程-JavaScript - CUBEKPIMEMBER函数

描述 该函数返回关键绩效指标(KPI)属性,并在单元格中显示KPI名称。 语法 CUBEKPIMEMBER (connection, kpi_name, kpi_property, [caption])争论 Argument描述Required/OptionalconnectionName of the connection to the cube - A text stringRequiredkpi_nameName of the K…

LuatOS 开发指南

NDK 开发 官方教程 官方例程 API 下载软件 下载官方NDK例程压缩包到本地&#xff0c;并解压。可以看到目录如下&#xff1a; doc: 文档教程 env: 编译环境 example: NDK示例 platform: 需要编译的平台&#xff08;air72x/air8xx&#xff09; tools: 其他辅助软件 VSCode 使…

设计模式-7--代理模式(Proxy Pattern)

一、什么是代理模式&#xff08;Proxy Pattern&#xff09; 代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许一个对象&#xff08;代理&#xff09;充当另一个对象&#xff08;真实对象&#xff09;的接口&#xff0c;以控制对该对象的…

【数据结构】 二叉树面试题讲解->叁

文章目录 &#x1f30f;引言&#x1f332;[根据二叉树创建字符串](https://leetcode.cn/problems/construct-string-from-binary-tree/submissions/)&#x1f431;‍&#x1f464;题目描述&#xff1a;&#x1f431;‍&#x1f409;示例&#xff1a;&#x1f4cc;示例一&#x…

Autofac中多个类继承同一个接口,如何注入?与抽象工厂模式相结合

多个类继承同一个接口,如何注入&#xff1f;与抽象工厂模式相结合 需求: 原来是抽象工厂模式,多个类继承同一个接口。 现在需要使用Autofac进行选择性注入。 Autofac默认常识: Autofac中多个类继承同一个接口,默认是最后一个接口注入的类。 解决方案&#xff1a;(约定大于配…

Scala的函数式编程与高阶函数,匿名函数,偏函数,函数的闭包、柯里化,抽象控制,懒加载等

Scala的函数式编程 函数式编程 解决问题时&#xff0c;将问题分解成一个一个的步骤&#xff0c;将每个步骤进行封装&#xff08;函数&#xff09;&#xff0c;通过调用这些封装好的步骤&#xff0c;解决问题。 例如&#xff1a;请求->用户名、密码->连接 JDBC->读取…

分布式session的4种解决方案

分布式session的4种解决方案 1、cookie和session cookie和session都是用来跟踪用户身份信息的会话方式。 cookie存储的数据保存在本地客户端&#xff0c;用户获取容易&#xff0c;但安全性不高&#xff0c;存储数据小。 session存储的数据保存在服务器&#xff0c;用户不易获取…

linux中busybox与文件系统的关系

busybox与文件系统 在 Linux 中&#xff0c;BusyBox 是一个精简的、多功能的工具集合&#xff0c;它包含了一系列常用的命令和实用程序&#xff0c;如 ls、cp、mkdir 等。BusyBox 的目标是提供一个功能完整而又占用空间较小的工具集合&#xff0c;适用于嵌入式系统或资源受限的…

安防监控视频平台EasyCVR视频汇聚平台定制项目增加AI智能算法详细介绍

安防视频集中存储EasyCVR视频汇聚平台&#xff0c;可支持海量视频的轻量化接入与汇聚管理。平台能提供视频存储磁盘阵列、视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、平台级联、H.265自动转码等功能。为了便…

hadoop学习:mapreduce入门案例四:partitioner 和 combiner

先简单介绍一下partitioner 和 combiner Partitioner类 用于在Map端对key进行分区 默认使用的是HashPartitioner 获取key的哈希值使用key的哈希值对Reduce任务数求模决定每条记录应该送到哪个Reducer处理自定义Partitioner 继承抽象类Partitioner&#xff0c;重写getPartiti…

udig下载、安装及汉化,生成geoserver图层样式sld文件

uDig是一款开源免费的桌面地理信息系统框架软件。uDig汉化版主要采用RCP技术构建&#xff0c;内置的多专业的水文工具&#xff0c;拥有复杂专业的分析能力&#xff0c;既可以作为独立程序运行&#xff0c;还可以作为插件使用。 uDig是一个 open source (EPL and BSD) 桌面应用程…

【高阶产品策略】设计有效的AB测试

文章目录 1、A/B测试概述2、A/B测试实施过程3、A/B测试中需要注意的地方4、从一个案例中看A/B测试 1、A/B测试概述 2、A/B测试实施过程 3、A/B测试中需要注意的地方 4、从一个案例中看A/B测试