C++:结构体和类

在之前的博客中已经讲过了C语言中的结构体概念了,重复的内容在这儿就不赘述了。C++中的结构体在C语言的基础上还有些补充,在这里说明一下,顺便简单地讲一下类的概念。

一、成员函数

结构体类型声明的关键字是 struct ,在C++中结构体类型声明的基本语法如下:
struct tag
{成员变量列表; //成员变量可以有1个,也可以有多个成员函数列表; //成员函数可以有,也可以没有
} 结构体变量列表; //在声明结构体类型的同时,创建结构体变量,可以有多个,中间使⽤逗号隔开

我们可以发现C++中的结构体和C语言结构体的有一个比较大的差异就是:C++中的结构体中除了有成员变量之外,还可以包含成员函数。下面先概要地介绍一下成员函数:

(1)C++的结构体会有一些默认的成员函数,比如:构造函数、析构函数等,是编译器默认生成的,如果觉得不合适,也是可以自己显示的定义这些函数,这些函数都是自动被调用,不需要手动调用。

(2)除了默认的成员函数之外,我们可以自己定义一些成员函数,这些成员函数可以有,也可以没有,完全根据的实际的需要来添加就可以。

(3)这些成员函数可以直接访问成员变量。

(4)成员函数的调用也使用 . 操作符。
1. 构造函数
构造函数是结构中默认的成员函数之一,构造函数的主要的任务是初始化结构体变量。写了构造函
数,就不需要再写其他成员函数来初始化结构体(类)的成员,而且构造函数是在结构变量创建的时候,编译器自动被调用的。
构造函数的特征如下:
(1)函数名与结构体(类)名相同。
(2)无返回值。
(3)构造函数可以重载。
(4)若未显式定义构造函数,系统会自动生成默认的构造函数。
2. 析构函数
析构函数是用来完成结构体变量中资源的清理工作,也是结构体中默认的成员函数之一。析构函数在结构体变量销毁的时候,被自动调用的。
析构函数的特征如下:
(1)析构函数名是在结构体(类)名前加上字符 ~
(2)无参数无返回值类型。
(3)一个类只能有一个析构函数。若未显式定义析构函数,系统会自动生成默认的析构函数。注意:析构函数不能重载。
#include <iostream>
using namespace std;
struct Stu
{//成员变量string name;int chinese;int math;int total; //构造函数Stu(){cout << "调用构造函数" << endl;name = "小明";chinese = 99;math = 95;total = math + chinese;}//析构函数~Stu(){cout << "调用析构函数" << endl;//对于当前这个类的对象没有啥资源清理}
};
int main()
{struct Stu s;return 0;
}

运行结果打印了调用构造函数和调用析构函数这两句话,这也验证了构造函数和析构函数是会被自动调用的。

3.自定义函数
创建结构体中的自定义函数和我们平时创建自定义函数的方式是一样的,只是在使用的时候需要使用 . 操作符。
#include <iostream>
using namespace std;
struct Stu
{//成员变量string name;int chinese;int math;int total;//构造函数Stu(){name = "小明";chinese = 99;math = 95;total = math + chinese;}void print_stu() //打印{cout << "名字:" << name << endl;cout << "语文:" << chinese << endl;cout << "数学:" << math << endl;cout << "总分:" << total << endl;}
};
int main()
{struct Stu s;s.print_stu();return 0;
}

二、运算符重载

我们在C++中打印数据的时候,习惯了直接使用  cout 来直接打印数据的。比如:
int n = 100;
float f = 3.14f;
cout << n << endl;
cout << f << endl;
那针对 struct Stu 类型的变量能不能直接使用  cout 来打印呢?当我们直接使用时会发现这样是行不通的。 那怎么解决呢?其实在C++中要针对自定义类型的变量,想使用  cout << 来输出变量的内容, 就得对 << 这个输出运算符进行重载。具体代码如下:
#include <iostream>
using namespace std;
struct Stu
{//成员变量string name;int chinese;int math;int total;
};
//重载输出运算符
ostream& operator<<(ostream& os, const Stu & s)
{os << "名字: " << s.name << endl;os << "语文: " << s.chinese << endl;os << "数学: " << s.math << endl;os << "总分: " << s.total << endl;return os;
}
int main()
{struct Stu s = {"张三", 90, 80, 170};cout << s << endl; return 0;
}

ostream&:表示该函数返回一个指向 ostream 对象的引用。ostream 是C++中用于输出流的标准库类,例如 cout 就是 ostream 类型的一个实例。
operator<<:这是重载运算符的语法,允许你定义当使用 `<<` 运算符时应该执行的操作。这里我们重载了 << 运算符,使其能够接受 ostream 和 Stu 类型的参数,并按特定格式输出对象的信息。
(ostream& os, const Stu & s):这个函数接受两个参数,第一个是 ostream 类型的引用 os,代表输出流(如 cout),第二个是一个常量引用 const Stu & s,即要输出的 Stu 类型对象。

接下来就简单地讲集几种运算符重载。

1.加号运算符重载

#include <iostream>
using namespace std;
struct Stu
{int a;float b;Stu operator+(Stu s){Stu s3;s3.a=a+s.a;return s3;}
}s1,s2;
int main()
{s1.a=1;s2.a=2;Stu s3=s1+s2;cout << s3.a;return 0;
}

在这段代码中,Stu operator+(Stu s) 是一个成员函数,用于重载加法运算符(+),以便两个 Stu 类型的对象可以相加。它接受一个 Stu 类型的参数 s 并返回一个 Stu 类型的结果。由于这是一个成员函数,它隐式地接收调用它的对象作为第一个参数。这个运算符重载为s1.operator+(s2),可以简化为s1+s2。当然我们也可以使用全局函数的方式来实现运算符的重载。

#include <iostream>
using namespace std;
struct Stu
{int a;float b;
}s1,s2;
Stu operator+(Stu s1,Stu s2)
{Stu s3;s3.a=s1.a+s2.a;return s3;
}
int main()
{s1.a=1;s2.a=2;Stu s3=s1+s2;cout << s3.a;return 0;
}

这个运算符重载为operator+(s1,s2),可以简化为s1+s2。

2.左移运算符重载

最上面举的cout的例子就是左移运算符重载,是采取全局函数的方式来实现的。在这里顺便一提,在这里如果我们使用成员函数的方式是实现不了的,在下面代码中我们可以看出,虽然能够重载这个运算符,但是实现的效果为s << cout ,并不是我们想要的结果。

#include <iostream>
using namespace std;
struct Stu
{//成员变量string name;int chinese;int math;int total;//重载输出运算符
ostream& operator<<(ostream& os)
{os << "名字: " << name << endl;os << "语文: " << chinese << endl;os << "数学: " << math << endl;os << "总分: " << total << endl;return os;
}
};
int main()
{struct Stu s = {"张三", 90, 80, 170};s << cout; return 0;
}

3.递增运算符重载

#include <iostream>
using namespace std;
struct Stu
{int a;//前置++重载 Stu& operator++(){a++;return *this;}//后置++重载Stu operator++(int){Stu s=*this;a++;return s;}
}s;//重载输出运算符
ostream& operator<<(ostream& os,const Stu& s)
{os << s.a << endl;return os;
}
void test1()
{cout << ++s;
}
void test2()
{cout << s++;
} 
int main()
{s.a=1;test1();s.a=1;test2();return 0;	
} 

在自增运算符的重载中许多思想是和之前一样的,区别在于前置运算符重载作为成员函数时没有额外参数,后置运算符重载作为成员函数时需要一个额外的、类型为int的占位参数。这个参数通常不使用,只是用来与前置运算符区分开来。还有一个注意的点是,在前置++重载时,我们把返回值的类型设为了结构体的引用,这是因为当我们对内置数据连续自增操作是对该数据连续自增的,说起来很奇怪,其实就是进行一次自增操作,变量就自增一次。但如果在这里我们不使用引用,就会返回一个结构体类型的临时变量,如果在进行自增操作,对于原来的结构体变量是没有影响的。而后置++是不会进行连续操作的,所以我们不需要考虑。

4.关系运算符重载

#include <iostream>
#include <string>
using namespace std;
struct Stu
{string name;int age;bool operator==(Stu& s){if(this->name==s.name&&this->age==s.age)return true;elsereturn false;}
};
int main()
{Stu s1={"Tom",12};Stu s2={"Tom",12};if(s1==s2)cout << "same" << endl;return 0;
}

三、结构体排序 - sort

说到排序,我们之前讲过冒泡排序,我们也可以写一个冒泡排序函数来排序一组结构体数据,但是这里给大家介绍一个 C++ 的 STL 中的库函数 sort ,可以直接用来排序数据,只要涉及到数据的排序,又没有明确要求自己实现排序算法的时候,就可以直接使用sort函数。
1.sort 函数介绍
函数原型如下:
//版本1
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
//void sort(开始位置,结束位置);
//first:指向要排序范围的第⼀个元素的迭代器或者指针。
//last:指向要排序范围的最后⼀个元素之后位置的迭代器或者指针。
//版本2
template <class RandomAccessIterator, class Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare 
comp);
//void sort(开始位置,结束位置,⾃定义排序函数);
//first:指向要排序范围的第⼀个元素的迭代器或者指针。
//last:指向要排序范围的最后⼀个元素之后位置的迭代器或者指针。
//comp:是⼀个⽐较函数或者函数对象
//这⾥开始位置和结束位置,可以是指针,也可以是迭代器
//⾃定义排序函数 可以是函数,也可以是仿函数
sort 函数需要包含的头文件 <algorithm>。在默认情况下 sort 函数,按升序对给定范围 [first,last)中的元素进行排序。
2.排序内置类型数据
对数组进行排序:
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{int arr[] = { 4,5,6,9,7,1,2,8,5,4,2 };int size = sizeof(arr) / sizeof(arr[0]);//起始位置和结束位置传的是地址sort(arr, arr + size);for (int i = 0; i < size; i++){cout << arr[i] << " ";}cout << endl;return 0;
}
默认排序结果是升序的。
对字符串中的字符进行排序:
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main()
{string s("defxxxabccba");sort(s.begin(), s.end());cout << s << endl;return 0;
}
这里是对字符串中字符的顺序进行排序,是按照字符的ASCII值进行排序的。默认排序的结果依然是升序。
3.自定义排序
sort 在默认的情况下是按照升序排序,如何按照降序排序呢?如果是结构体类型数据进行排序呢?
怎么办?我们可以使用自定义排序方式:
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare 
comp);
sort 的第三个参数是一个可选的自定义比较函数(或函数对象),用于指定排序的规则。如果不提
供这个参数, std::sort 默认会使用小于运算符 ( < ) 来比较元素,并按照升序排序。这个比较函数,接受两个参数,并返回一个布尔值。如果第一个参数应该排在第二个参数之前,则返回 true;否则返回 false。
接下来试试看,comp 表示可以自定义一个排序方法,使用方法如下:
#include <iostream>
#include <algorithm>
using namespace std;
//自定义一个比较函数,这个⽐较函数能够⽐较被排序数据的2个元素大小
//函数返回bool类型的值
bool compare(int x, int y) 
{return x > y;//排降序
}
int main()
{int arr[] = { 4,5,6,9,7,1,2,8,5,4,2 };int size = sizeof(arr) / sizeof(arr[0]);//将函数名作为第三个参数传⼊sort函数中sort(arr, arr + size, compare);for (int i = 0; i < size; i++){cout << arr[i] << " ";}cout << endl;return 0;
}

当然第三个参数还可以使用结构体中重载()运算符-仿函数。

#include <iostream>
#include<algorithm>
using namespace std;
//仿函数⽅式 - 仿函数也叫函数对象
struct Cmp
{bool operator()(int x, int y){return x > y;//排降序}
}cmp;
int main()
{int arr[] = { 4,5,6,9,7,1,2,8,5,4,2 };int size = sizeof(arr) / sizeof(arr[0]);//将结构体对象作为第三个参数传⼊sort函数中sort(arr, arr + size, cmp);for (int i = 0; i < size; i++){cout << arr[i] << " ";}cout << endl;return 0;
}

四、类

C++中为了更好的实现面向对象,更喜欢使用  class (类)来替换 struct (结构体)。你可以简单粗暴
的理解为 class struct 是一回事,但是其实本上还是有区别的。
1. 类定义
类的关键字是 class ,类中主要也是由两部分组成,分别为成员变量和成员函数。形式如下:
class tag
{
public:成员变量列表;成员函数列表;
}; // ⼀定要注意后⾯的分号
class 是用来定义类类型的关键字,在类中可以定义成员函数和成员变量。
public 是类成员权限访问限定符,标志类中的成员可以公开访问及调用。
在这里我们就能发现一个与结构体不同的地方,结构体中没有public。这是为什么呢? 原因是:结构体的成员变量和成员函数,默认就是公开的,而 class 中的成员变量和成员 函数默认是私有的( private )。
class Stu
{
public: void init_stu() {name= "⼩明";chinese = 0;math = 0;total = 0;}string name; //名字int chinese; //语⽂成绩int math; //数学成绩int total; //总成绩
};
2.类的使用
(1) 创建类对象
int main()
{Stu s1; //创建类对象s1Stu s2; //创建类对象s2return 0;
}

(2)调用类对象成员

像结构体一样,通过操作符( . )即可对类对象的成员进行访问。
#include <iostream>
using namespace std;
int main()
{//创建类对象Stu s1;//调⽤成员变量s1.name = "张三";s1.chinese = 90;s1.math = 98;s1.total = s1.chinese + s1.math;Stu s2;//调⽤成员函数s2.init_stu();return 0;
}

3.访问权限控制

访问限定符是C++语言中的关键字,用于指定类成员的访问权限。
访问限定符主要有三个:
public :成员被声明为 public 后,可以被该类的任何方法访问,包括在类的外部。
protected :成员被声明为 protected 后,可以被该类访问。
private :成员被声明为 private 后,只能被该类的成员函数访问。
#include <iostream>
using namespace std;class Stu
{
public: //将成员函数设置为公有属性void init_stu() {name= "⼩明";chinese = 0;math = 0;total = 0;}
private: //将成员变量设置为私有属性string name; //名字int chinese; //语⽂成绩int math; //数学成绩int total; //总成绩
};
int main()
{Stu stu;stu.init_stu(); //访问公有成员函数cout << stu.name << endl; //访问私有成员变量,编译器报错return 0;
}
通过运行结果可见,类外可直接访问设置为公有的成员,而不可以直接访问私有的成员。 习惯上,外部可访问的成员函数通常设置为公有属性( public ),而为了提高成员变量的 访问安全性,通常将成员变量设置为私有属性( private ),即只有类内部可以访问。

4.结构体和类的区别

C++中 struct class 的区别是什么?
C++兼容了C语言,所以C++中 struct 既可以当成结构体使用,还可以用来定义类,和 class 定义
类是一样的。区别是 struct 定义的类默认访问权限是 public class 定义的类默认访问权限是 private
类的定义既可以使用  struct ,也可以使用  class。C++更常用 class 关键词来定义类,因此在选用  class 的时候需要加上访问限定符 public 才可以在类外调用类的成员函数和成员变量。

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

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

相关文章

redis底层数据结构

底层数据结构 了解下这些咱常用的数据其底层实现是啥 在提到使用哪类数据结构之前&#xff0c;先来了解下redis底层到底有多少种数据结构 1&#xff0c;sds动态字符串 概念与由来 redis是一种使用C语言编写的nosql&#xff0c;redis存储的key数据均为string结构&#xff0…

ChatGPT怎么回事?

纯属发现&#xff0c;调侃一下~ 这段时间deepseek不是特别火吗&#xff0c;尤其是它的推理功能&#xff0c;突发奇想&#xff0c;想用deepseek回答一些问题&#xff0c;回答一个问题之后就回复服务器繁忙&#xff08;估计还在被攻击吧~_~&#xff09; 然后就转向了GPT&#xf…

趣味Python100例初学者练习01

1. 1 抓交通肇事犯 一辆卡车违反交通规则&#xff0c;撞人后逃跑。现场有三人目击该事件&#xff0c;但都没有记住车号&#xff0c;只记下了车号的一些特征。甲说&#xff1a;牌照的前两位数字是相同的&#xff1b;乙说&#xff1a;牌照的后两位数字是相同的&#xff0c;但与前…

2024-我的学习成长之路

因为热爱&#xff0c;无畏山海

蓝桥杯备考:高精度算法之除法

我们除法的高精度其实也不完全是高精度&#xff0c;而是一个高精度作被除数除以一个低精度 模拟我们的小学除法 由于题目中我们的除数最大是1e9&#xff0c;当它真正是1e9的时候&#xff0c;t是有可能超过1e9的&#xff0c;所以要用long long

Maven jar 包下载失败问题处理

Maven jar 包下载失败问题处理 1.配置好国内的Maven源2.重新下载3. 其他问题 1.配置好国内的Maven源 打开⾃⼰的 Idea 检测 Maven 的配置是否正确&#xff0c;正确的配置如下图所示&#xff1a; 检查项⼀共有两个&#xff1a; 确认右边的两个勾已经选中&#xff0c;如果没有请…

【前端】ES6模块化

文章目录 1. 模块化概述1.1 什么是模块化?1.2 为什么需要模块化? 2. 有哪些模块化规范3. CommonJs3.1 导出数据3.2 导入数据3.3 扩展理解3.4 在浏览器端运行 4.ES6模块化4.1 浏览器运行4.2 在node服务端运行4.3 导出4.3.1 分别导出4.3.2 统一导出4.3.3 默认导出4.3.4 混用 4.…

强化学习笔记(5)——PPO

PPO视频课程来源 首先理解采样期望的转换 变量x在p(x)分布下&#xff0c;函数f(x)的期望 等于f(x)乘以对应出现概率p(x)的累加 经过转换后变成 x在q(x)分布下&#xff0c;f(x)*p(x)/q(x) 的期望。 起因是&#xff1a;求最大化回报的期望&#xff0c;所以对ceta求梯度 具体举例…

20-30 五子棋游戏

20-分析五子棋的实现思路_哔哩哔哩_bilibili20-分析五子棋的实现思路是一次性学会 Canvas 动画绘图&#xff08;核心精讲50个案例&#xff09;2023最新教程的第21集视频&#xff0c;该合集共计53集&#xff0c;视频收藏或关注UP主&#xff0c;及时了解更多相关视频内容。https:…

【HTML入门】Sublime Text 4与 Phpstorm

文章目录 前言一、环境基础1.Sublime Text 42.Phpstorm(1)安装(2)启动Phpstorm(3)“启动”码 二、HTML1.HTML简介(1)什么是HTML(2)HTML版本及历史(3)HTML基本结构 2.HTML简单语法(1)HTML标签语法(2)HTML常用标签(3)表格(4)特殊字符 总结 前言 在当今的软件开发领域&#xff0c…

Kubernetes学习之包管理工具(Helm)

一、基础知识 1.如果我们需要开发微服务架构的应用&#xff0c;组成应用的服务可能很多&#xff0c;使用原始的组织和管理方式就会非常臃肿和繁琐以及较难管理&#xff0c;此时我们需要一个更高层次的工具将这些配置组织起来。 2.helm架构&#xff1a; chart:一个应用的信息集合…

Kamailio 不通过 dmq 实现注册复制功能

春节期间找到一篇文章&#xff0c;需要 fg 才能看到&#xff1a; https://medium.com/tumalevich/kamailio-registration-replication-without-dmq-65e225f9a8a7 kamailio1 192.168.56.115 kamailio2 192.168.56.116 kamailio3 192.168.56.117 route[HANDLE_REPLICATION] {i…

grpc 和 http 的区别---二进制vsJSON编码

gRPC 和 HTTP 是两种广泛使用的通信协议&#xff0c;各自适用于不同的场景。以下是它们的详细对比与优势分析&#xff1a; 一、核心特性对比 特性gRPCHTTP协议基础基于 HTTP/2基于 HTTP/1.1 或 HTTP/2数据格式默认使用 Protobuf&#xff08;二进制&#xff09;通常使用 JSON/…

Intel 与 Yocto 项目的深度融合:全面解析与平台对比

在嵌入式 Linux 领域&#xff0c;Yocto 项目已成为构建定制化 Linux 发行版的事实标准&#xff0c;广泛应用于不同架构的 SoC 平台。Intel 作为 x86 架构的领导者&#xff0c;在 Yocto 生态中投入了大量资源&#xff0c;为其嵌入式处理器、FPGA 和 AI 加速硬件提供了完整的支持…

kubernetes(二)

文章目录 NamespacePodLabelDeploymentService Namespace 在Kubernetes系统中&#xff0c;Namespace是一种至关重要的资源类型&#xff0c;其主要功能在于实现多套环境的资源隔离或者多租户的资源隔离&#xff0c;默认情况下所有的Pod都能够相互访问&#xff0c;但如果不想让两…

巧妙利用数据结构优化部门查询

目录 一、出现的问题 部门树接口超时 二、问题分析 源代码分析 三、解决方案 具体实现思路 四、优化的效果 一、出现的问题 部门树接口超时 无论是在A项目还是在B项目中&#xff0c;都存在类似的页面&#xff0c;其实就是一个部门列表或者叫组织列表。 从页面的展示形式…

【数据分析】案例04:豆瓣电影Top250的数据分析与Web网页可视化(numpy+pandas+matplotlib+flask)

豆瓣电影Top250的数据分析与Web网页可视化(numpy+pandas+matplotlib+flask) 豆瓣电影Top250官网:https://movie.douban.com/top250写在前面 实验目的:实现豆瓣电影Top250详情的数据分析与Web网页可视化。电脑系统:Windows使用软件:PyCharm、NavicatPython版本:Python 3.…

【线程】基于环形队列的生产者消费者模型

1 环形队列 环形队列采用数组来模拟&#xff0c;用取模运算来模拟环状特性。 1.如何判断环形队列为空或者为满? 当环形队列为空时&#xff0c;头和尾都指向同一个位置。当环形队列为满时&#xff0c;头和尾也都指向同一个位置。 因此&#xff0c; 可以通过加计数器或者标记…

Vue指令v-html

目录 一、Vue中的v-html指令是什么&#xff1f;二、v-html指令与v-text指令的区别&#xff1f; 一、Vue中的v-html指令是什么&#xff1f; v-html指令的作用是&#xff1a;设置元素的innerHTML&#xff0c;内容中有html结构会被解析为标签。 二、v-html指令与v-text指令的区别…

OPENGLPG第九版学习 - 着色器基础

文章目录 2.1 着色器与OpenGL2.2 0penGL的可编程管线2.3 OpenGL着色语言GLSL概述2.3.1 使用GLSL构建着色器变量的声明变量的作用域变量的初始化构造函数 、 类型转换聚合类型访问向量和矩阵中的元素结构体数组多维数组 2.3.2 存储限制符const 存储限制符in 存储限制符out 存储限…