当前位置: 首页 > article >正文

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;
		else
			return 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; //创建类对象s1
 Stu s2; //创建类对象s2
 return 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.kler.cn/a/530915.html

相关文章:

  • csapp笔记3.6节——控制(1)
  • 优化代码性能:利用CPU缓存原理
  • FLTK - FLTK1.4.1 - demo - bitmap
  • XML Schema 数值数据类型
  • LeetCode:121.买卖股票的最佳时机1
  • 揭秘算法 课程导读
  • 刷题记录 动态规划-5: 62. 不同路径
  • python的pre-commit库的使用
  • leetcode——从前序与中序遍历序列构造二叉树(java)
  • stm32小白成长为高手的学习步骤和方法
  • NOTEPAD++编写abap
  • 国土安全保障利器,高速巡飞无人机技术详解
  • CMD模块
  • 嵌入式八股文面试题(一)C语言部分
  • 如何处理 Typecho Joe 主题被抄袭或盗版的问题
  • SpringBoot源码解析(九):Bean定义接口体系
  • 【挖矿——前缀和】
  • 网络工程师 (15)产权人确定
  • ip归属地是不是要打开定位?
  • 【SLAM】于ubuntu18.04上纯CPU运行GCNv2_SLAM的记录(ARM64/AMD64)
  • 图 、图的存储
  • 实现数组的扁平化
  • deepseek-r1模型本地win10部署
  • 使用 SpringBoot+Thymeleaf 模板引擎进行 Web 开发
  • SRS代码目录
  • 中间件的概念及基本使用