C++蓝桥杯之结构体10.15

一. 结构体

结构体是⼀种自定义的类型,使用这种自定义类型可以描述一些复杂对象。 前面我们学习的都是单一的数据类型,比如:char 、 short 、 int 、 double 类型,但是我们现实生活中总是有写复杂对象,比如:人、书等,这些复杂对象,仅仅使用单一的数据类型是不能描述的。比如:描述一本书,书有作者、出版社、定价等信息;描述一个人,人有名字、性别、年龄、 身高、体重等信息。那怎么描述这些复杂对象呢? C++中引入了结构体和类来解决这些问题。在C++中结构体和类是相似的,这里首先介绍结构体。

1. 结构体类型声明和变量定义

结构体类型声明的关键字是 struct ,结构体类型声明的基本语法如下:

cpp 复制代码
struct tag
 {
    成员变量列表; 
	//成员变量可以有1个,也可以有多个
    成员函数列表;     
    //成员函数可以有,也可以没有
 
} 
    结构体变量列表; 
	//在声明结构体类型的同时,创建结构体变量,可以有多个,
	//中间使用逗号隔开
 

像这样

cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

struct Stu  //--学生类型 
{
	string name;
	int age;
	int chinese;
	int math;
	int total;
 } ;

这就是我们声明的结构体类型

struct Stu s1; ---这是结构体变量的创建

当我们声明结构体后就可以使用其来创建变量

cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

struct Stu  //--学生类型 
{
	string name;
	int age;
	int chinese;
	int math;
	int total;
 }s4,s5,s6;//这里的s4,s5,s6,就是在结构体声明的同时,
 //顺便创建的三个变量 ,是全局变量

struct Stu s7;
//全局变量 
 
int main()
{
	struct Stu s1;
	struct Stu s2;
	Stu s3;  //struct (在创建变量时)可省略 
	Stu arr[6];//结构体类型的数组 
	
	return 0;
 } 

总结为

1. 在创建变量的时候,结构体类型中的 struct 就可以省略了。

2. 在创建结构体变量的时候,结构体变量的名字不能和结构体类型的名字相同。

3. 声明的结构体类型是不占用内存空间,当使用结构体类型创建了变量后,才向内存申请了空间的。

(变量创建的本质是:在内存中申请空间,如int a = 10 , 是向内存申请了4个字节空间,用来存放整数10,int是不占用空间的,所以类型不占用空间,只有创建变量时才会向内存申请空间)

4. 结构体变量可以是全局变量,也可以是局部变量。

2. 结构体变量的特点

结构体的初始化

结构体的初始化和数组类似,使用 {} ,将初始化的内容按照顺序放在 {} 中就可以。

cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

struct Stu  //--学生类型 
{
	string name;
	int chinese;
	int math;
	int total;
 };

int main()
{
	struct Stu s1 = {"zhangsan",85,95,0};
	
	//调试时,确实能看到我们创建的变量被存入了类型中 
	
	
	return 0;
 } 

这便是初始化

结构体整体操作

结构体变量中虽然包含多个成员,而且成员可能是不同类型的。 但是一个结构体变量可以看做一个整体,是可以直接进行赋值操作的。

cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

struct Stu  //--学生类型 
{
	string name;
	int chinese;
	int math;
	int total;
 };

int main()
{
	struct Stu s1 = {"zhangsan",85,95,0};
	
	Stu s2;
	s2 = s1;
	//进行调试监视s2时,能发先第一遍s2的值是随机的
	//第二遍则为s1的值 
	
	return 0;
 } 

结构体成员的访问

结构体成员访问的基本形式是:

cpp 复制代码
结构体变量.成员名

因为每个结构体变量中都有属于自己的成员,所有必须使用 . 这种结构体成员访问操作符。 通过这种方式找到成员后,可以直接给结构体变量的成员中输入值,或者直接存储和使用。

cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

struct Stu  //--学生类型 
{
	string name;
	int chinese;
	int math;
	int total;
 };

int main()
{
	Stu s1; 
	s1.name = "lisi";
	s1.chinese = 90;
	cin >> s1.math;
	//输入92 
	s1.total = s1.chinese + s1.math;
	cout << s1.total << endl;
	//输出182 
	//使用调试-监视可以看到数值的变换 
	
	return 0;
 } 

结构体嵌套

当然如果结构体中嵌套了其他结构体成员,这里初始化的时候,也可以员中的成员可以连续使用 . 操作符。

例如:

cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

struct Score
{
    int chinese;
    int math;
    int english;
 };
 
struct Stu
{
    string name;
    Score sc;//三门成绩 
    int total;//总 
    int avg;//平均 
 };
 
int main()
{
	Stu s1 = { "wangwu", {80,90,60}, 0, 0 };
	//结构体里包含嵌套
	s1.total = s1.sc.chinese + s1.sc.english + s1.sc.math; 
	s1.avg = s1.total / 3;
	cout << s1.total << " " << s1.avg << endl;
	//输出 230 76 
	
	return 0;
 }

这便是结构体的嵌套

3. 结构体成员函数

以上我们讲的是结构体的基本知识,C语言中的结构体就是这样的,但是C++中的结构体和C语言结构体的有一个比较大的差异就是:

C++中的结构体中除了有成员变量之外,还可以包含成员函数。

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

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

  3. 这些成员函数可以直接访问成员变量

  4. 成员函数的调用也使用**.**操作符

代码举例

cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

 
 struct Stu  
{
	//成员变量 
	string name;
	int chinese;
	int math;
	int total;
	//成员函数
	//初始化结构体的成员变量
	void init_stu()
	{
		name = "小明";
		chinese = 100;
		math = 100;
		total = chinese + math;
	 } 
	  
	 
 };
 
 
int main()
{
	struct Stu s1;
	s1.init_stu();
	
	return 0;	
}
 

调试时就能观察到该函数完成了结构体的初始化和调用

cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

 
 struct Stu  
{
	//成员变量 
	string name;
	int chinese;
	int math;
	int total;
	//成员函数
	//初始化结构体的成员变量
	void init_stu()
	{
		name = "小明";
		chinese = 100;
		math = 100;
		total = chinese + math;
	 } 
	  
	 
 };
 
 
int main()
{
	struct Stu s1;
	s1.init_stu();
	cout << "名字:" << s1.name << endl; 
	cout << "语文:" << s1.chinese << endl;
	cout << "数学:" << s1.math << endl;
	cout << "总分:" << s1.total << endl;
	//输出 
	//名字:小明
    //语文:100
    //数学:100
    //总分:200
	
	return 0;	
}
cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

 
struct Stu  
{
	//成员变量 
	string name;
	int chinese;
	int math;
	int total;
	//成员函数
	//初始化结构体的成员变量
	void init_stu()
	{
		name = "小明";
		chinese = 100;
		math = 100;
		total = chinese + math;
	 } 
	
	//打印的信息 
    void print_stu()  //--成员函数 
{
	cout << "名字:" << name << endl; 
	cout << "语文:" << chinese << endl;
	cout << "数学:" << math << endl;
	cout << "总分:" << total << endl;
 }  
	 
 };
 

int main()
{
	struct Stu s1;
	s1.init_stu();
	s1.print_stu(); 
	
	//输出 
	//名字:小明
    //语文:100
    //数学:100
    //总分:200
	
	return 0;	
}

上面的void都是成员函数

构造函数和析构函数

构造函数:

构造函数是结构中默认的成员函数之一,构造函数的主要的任务是初始化结构体变量。写了构造函 数,就不需要再写其他成员函数来初始化结构体(类)的成员,而且构造函数是在结构变量创建的时候, 编译器⾃动被调用的。

构造函数的特征如下:

• 函数名与结构体(类)名相同。

• 无返回值。

• 构造函数可以重载。

• 若未显式定义构造函数,系统会自动生成默认的构造函数。

析构函数:

析构函数是用来完成结构体变量中资源的清理工作,也是结构体中默认的成员函数之一。

析构函数在结构体变量销毁的时候,被自动调用的。

析构函数的特征如下:

• 析构函数名是在结构体(类)名前加上字符

• 无参数无返回值类型。

• 一个类只能有一个析构函数。若未显式定义析构函数,系统会自动生成默认的析构函数。

注意:析构函数不能重载。

//代码-演示构造和析构函数

cpp 复制代码
//代码-演示构造和析构函数
 
#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 << "调用析构函数:~Stu()" << endl;
        //对于当前这个类的对象没有啥资源清理
        chinese = 0;
 
    }
    
    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; 
 } 
 
 //输出
 //调用构造函数
   //名字:小明
 //语文:99
 //数学:95
 //总分:194
 //调用析构函数:~Stu() 
 
 

构造函数和析构函数是编译器主动调用的

这里给大家简单的举例演示了一下结构体的成员函数,其实成员函数和自定义函数是一样的,是可以根据实际的需要来完成编写的。 当然在竞赛的题目中关于结构体其实很少使用成员函数,使用最多的应该是针对结构变量的运算符的重载。

4. 运算符的重载

在上一个代码的例子中,在打印结构体成员信息的时候,我们是通过 的。 但是我们在C++中打印数据的时候,习惯了直接使用 s.print_stu() 的方式完成 cout 来直接打印数据的。

比如:

cpp 复制代码
int n = 100;
float f = 3.14f;
cout << n << endl;
cout << f << endl;

那针对 struct Stu 类型的变量能不能直接使用 cout 来打印呢?

比如这样:

cpp 复制代码
#include <iostream>


using namespace std;

struct Stu
{
    //成员变量cout 来打印呢??如这样:
    string name;
    int chinese;
    int math;
    int total;
};

int main()
{
    struct Stu s = {"张三", 90, 80, 170};
    cout << s << endl; 
    return 0;
}

报错了,报错没有与这些操作数匹配的输出运算符,直接使用cout是不可以的

那这样需要对它进行重载

cpp 复制代码
#include <iostream>

using namespace std;

struct Stu
{
 //成员变量
 
    string name;
    int chinese;
    int math;
    int total;
};
 
 //重载运算符
 //重载的是<<(输出运算符),
 //让<< 支持struct stu 类型数据的打印 
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;
 }
//输出
//名字: 张三
//语文: 90
//数学: 80
//总分: 170 

5. 结构体排序-sort

说到排序,我们之前讲过冒泡排序,我们也可以写一个冒泡排序函数来排序一组结构体数据,但是这里给大家介绍一个C++的STL中的库函数 sort ,可以直接用来排序数据,在算法竞赛和日常开发中使用非常频繁。 只要涉及到数据的排序,又没有明确要求自己实现排序算法的时候,就可以直接使用sort函数。

sort 函数介绍

函数原型如下:

cpp 复制代码
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
 //void sort(开始位置,结束位置); 
//first:指向要排序范围的第一个元素的迭代器或者指针。
 
//last:指向要排序范围的最后一个元素之后位置的迭代器或者指针。

在默认情况下sort函数,按升序对给定范围[first, last ] 的元素进行排序。

sort函数需要包含的头文件 <algorithm>

还有另一种版本

cpp 复制代码
template <class RandomAccessIterator, class Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
 //void sort(开始位置,结束位置,自定义排序函数); 
 //first:指向要排序范围的第一个元素的迭代器或者指针。
 
 //last:指向要排序范围的最后一个元素之后位置的迭代器或者指针。
 
//comp:是一个比较函数或者函数对象
 
//这里开始位置和结束位置,可以是指针,也可以是迭代器
 
//自定义排序函数以是函数,也可以是仿函数

排序内置类型数据

对数组进行排序
cpp 复制代码
#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]);
    //起始位置和结束位置传的是地址
    //数组名就是首元素的地址
	//arr + sz 跳过sz个元素,就是sz+1个元素的地址 
 
    sort(arr, arr + size);
    for (int i = 0; i < size; i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
    //输出
	//1 2 2 4 4 5 5 6 7 8 9 
    
    return 0;
}

默认排序结果是升序的。

对字符串中的字符进行排序
cpp 复制代码
#include <iostream>
#include <algorithm>

using namespace std;

int main()
{
    string s("defxxxabccba");
    sort(s.begin(), s.end());
    cout << s << endl;
    //输出
	//aabbccdefxxx 
    
    return 0;
 }

这里是对字符串中字符的顺序进行排序,是按照字符的ASCII值进行排序的

默认排序的结果依然是升序。

自定义排序

sort在默认的情况下是按照升序排序,如何按照降序排序呢?如果是结构体类型数据进行排序呢? 怎么办? 使用自定义排序方式

cpp 复制代码
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare 
comp);

sort 的第三个参数是个一可选的自定义比较函数(或函数对象),用于指定排序的规则。如果不提 供这个参数, std::sort 默认会使用小于运算符( < )来比较元素,并按照升序排序。 这个比较函数,接受两个参数,并返回一个布尔值。如果第一个参数应该排在第二个参数之前,则返回true;否则返回 false。

comp 表示可以自定义一个排序方法,使用方法如下:

创建比较函数
cpp 复制代码
#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;
	//输出 9 8 7 6 5 5 4 4 2 2 1 
	
	return 0;
 } 

就是构造一个比较函数 compare,然后函数名传入参数,实现调用

结构体中重载()运算符-仿函数
cpp 复制代码
#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;
    //输出
	// 9 8 7 6 5 5 4 4 2 2 1
    
    return 0;
 }

关于仿函数、操作符重载,要深入学习C++,竞赛中涉及的较少、也比较浅。

排序结构体数据

两个结构体数据也是不能直接比较⼤大小的,在使用 sort 函数排序的时候,也是需要提供自定义的比较方法。比如:

使用名字

cpp 复制代码
#include <iostream>
#include <algorithm>
 
using namespace std;
 
struct S
 {
    string name;
    int age;
 };

//提交比较函数 

bool cmp_s_by_name(const struct S& s1, const struct S& s2)
{
    return s1.name > s2.name;
	//按名字降序
}
  
int main()
{
    struct S arr[] = {{"zhangsan",20},{"lisi",25},{"wangwu",18}};
	int sz = sizeof(arr) / sizeof(arr[0]);
	sort(arr,arr + sz,cmp_s_by_name );  
	for(int i = 0;i < sz; i++)
	{
		cout << arr[i].name << ":" <<arr[i].age << endl;
	}
    
    return 0;
 }

//输出
// zhangsan:20
// wangwu:18
// lisi:25

使用年龄

cpp 复制代码
#include <iostream>
#include <algorithm>
 
using namespace std;
 
struct S
 {
    string name;
    int age;
 };

//提交比较函数 

bool cmp_s_by_age(const struct S& s1, const struct S& s2)
{
    return s1.age > s2.age;
	//按年龄降序
}
  
int main()
{
    struct S arr[] = {{"zhangsan",20},{"lisi",25},{"wangwu",18}};
	int sz = sizeof(arr) / sizeof(arr[0]);
	sort(arr,arr + sz,cmp_s_by_age );  
	for(int i = 0;i < sz; i++)
	{
		cout << arr[i].name << ":" <<arr[i].age << endl;
	}
    
    return 0;
 }

//输出
// lisi:25
// zhangsan:20
// wangwu:18

使用仿函数的方式

cpp 复制代码
#include <iostream>
#include <algorithm>
 
using namespace std;

struct S 
 {
    string name;
    int age;
 };

//按照仿函数的方式来名字降序  

struct Cmp
{
	bool operator()(struct S s1, struct S s2)
	{
	    return s1.name > s2.name;
		//按名字降序
	}
}cmp;
  
int main()
{
    struct S arr[] = {{"zhangsan",20},{"lisi",25},{"wangwu",18}};
	int sz = sizeof(arr) / sizeof(arr[0]);
	sort(arr,arr + sz,cmp );  
	for(int i = 0;i < sz; i++)
	{
		cout << arr[i].name << ":" <<arr[i].age << endl;
	}
    
    return 0;
 }

//输出
// zhangsan:20
// wangwu:18
// lisi:25

按年龄只需name改为age,升序<,降序>

二. (拓展)类

C++中为了更好的实现面向对象,更喜欢使用的理解为 class 和 class (类)来替换 struct (结构体)。你可以简单粗暴 struct 是⼀回事,但是其实本上还是有区别的。接下来我们学习一下类。

1. 类的定义

类的关键字是 class ,类中主要也是由两部分组成,分别为成员变量和成员函数。

形式如下:

cpp 复制代码
class tag
{
    public:
    成员变量列表;
    成员函数列表;
}; 
// 一定要注意后面的分号

class 是用来定义类类型的关键字,在类中可以定义成员函数和成员变量。

public 是类成员权限访问限定符,标志类中的成员可以公开访问及调用,访问限定符在后续内容中 会介绍。

问:为什么结构体中没有 public 呢?

原因是:结构体的成员变量和成员函数,默认就是公开的,而函数默认是私有的( private )。

• 包含成员变量的类,例如描述一个学生:

cpp 复制代码
class Stu
{
    public:
    int math;   
	//数学成绩 
    string name;
	//名字
    int chinese; //语文成绩
    int total;   
	//总成绩
 };

包含成员变量和成员函数的类,如:

cpp 复制代码
class Stu
{
public:    
    void init_stu() 
    {
        name= "小明";
        chinese = 0;
        math = 0;
        total = 0;
    }
    
    string name; //名字
    int math;  //数学成绩  
    int chinese; //语文成绩
    int total;   //总成绩
};

在类中,成员变量和成员函数都是没有数量限制的,即可以有多个成员变量或成员函数

2. 类的使用

2.1. 创建对象

cpp 复制代码
int main()
{
    Stu s1;    
    Stu s2;    
    return 0;
 }

2.2. 调用类对象成员

像结构体⼀样,通过操作符(.)即可对类对象的成员进行访问

cpp 复制代码
#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 后,只能被该类的成员函数访问。

cpp 复制代码
#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 既可以当成结构体使用,还可以用来定义类,和 struct 定义的类默认访问权限是 private 。

结论:

类的定义既可以使用 public , class 定义 class 定义的类默认访问权限 struct ,也可以使用 class C++更常用 class 关键词来定义类,因此在选用 class 的时候需要加上访问限定符 public 才可以在类外调用类的成员函数和成员变量。

C++中关于类和对象的知识,远不止这些,类和对象的相关知识,在算法竞赛很少使用,但是在真正的软件开发过程中非常重要,C++的类和对象是C++面向对象思想重要组成体现,下来 一定要深入学习和挖掘。

好咯,uu们,写了几天也是终于搞定啦~

相关推荐
深思慎考6 小时前
调用百度云语音识别服务——实现c++接口识别语音
c++·语音识别·百度云
麦麦鸡腿堡6 小时前
Java的单例设计模式-饿汉式
java·开发语言·设计模式
简单点了6 小时前
go前后端项目的启动 、打包和部署
开发语言·后端·golang
爱吃山竹的大肚肚6 小时前
@Valid校验 -(Spring 默认不支持直接校验 List<@Valid Entity>,需用包装类或手动校验。)
java·开发语言
汤姆yu6 小时前
2026版基于python的协同过滤音乐推荐系统
开发语言·python
汤姆yu6 小时前
基于python的电子商务管理系统
开发语言·python
我是大咖7 小时前
C语言-贪吃蛇项目开发工具篇---ncursee库安装
c语言·开发语言
K 旺仔小馒头7 小时前
优选算法:01 双指针巧解移动零问题
c++·算法·刷题
weixin_445476688 小时前
Java并发编程——synchronized的实现原理与应用
java·开发语言·并发·synchronized