C++复习笔记6

news2024/11/18 4:43:37

 1.String类的实现

注意深浅拷贝, C语言字符串拼接函数strcat()

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vld.h>
#include<assert.h>
using namespace std;

class String
{
	friend ostream& operator<<(ostream & cout, const String & s);
public:
	//String(const char* str)//处理了空指针,等效空串进行初始化
	//{
	//	if (str == nullptr)
	//	{
	//		this->m_data = (char*)malloc(sizeof(char));
	//		this->m_data[0] = '\0';
	//	}
	//	else
	//	{
	//		this->m_data = (char*)malloc(sizeof(char) * (strlen(str) + 1));
	//		strcpy(this->m_data, str);
	//	}
	//}

	String(const char* str = "")//等效写法 空指针和空串不同 没有处理空指针,更加严格
	{
		this->m_data = (char*)malloc(sizeof(char) * (strlen(str) + 1));
		strcpy(this->m_data, str);
	}

	String(const String& s)
	{
		this->m_data = (char*)malloc(sizeof(char) * (strlen(s.m_data) + 1));
		strcpy(this->m_data, s.m_data);
	}

	String& operator=(const String& s)
	{
		if (this != &s)
		{
			free(this->m_data);

			this->m_data = (char*)malloc(sizeof(char) * (strlen(s.m_data) + 1));
			strcpy(this->m_data, s.m_data);
		}
		return *this;
	}

	int Length() const
	{
		return strlen(this->m_data);
	}

	String operator+(const String& s)
	{
		String tmp(*this);
		tmp += s;
		return tmp;
	}

	String& operator+=(const String& s)
	{
		char* tmp = (char*)malloc(strlen(this->m_data) + strlen(s.m_data) + 1);
		strcpy(tmp, this->m_data);
		free(this->m_data);
		this->m_data = (char*)malloc(strlen(tmp) + strlen(s.m_data) + 1);
		strcat(tmp, s.m_data);
		strcpy(this->m_data, tmp);
		free(tmp);
		return *this;
	}

	char operator[](int pos) const
	{
		assert(pos >= 0 && pos < Length());
		return *(this->m_data + pos);
		//return this->m_data[pos];
	}

	~String()
	{
		if (this->m_data != nullptr)
		{
			free(this->m_data);
			this->m_data = nullptr;
		}
	}
private:
	char* m_data;
};

ostream& operator<<(ostream& cout, const String& s)
{
	cout << s.m_data << endl;
	return cout;
}

void test01()
{
	//String s(nullptr);//空指针不能拿来给字符串初始化或者赋值
	String s1("a,b,c");

	String s2 = s1;
}

void test02()
{
	String s1("abc");
	String s2("xyz");
}

void test03()
{
	const char* pstr = "ABCXYZ";
	cout << *(pstr + 1) << endl;
	cout << pstr[1] << endl;
	String s("ABC");
	cout << s[2] << endl;
}

void test04()
{
	String s1("abc");
	String s2("xyz");
	s1 += s2;
	cout << s1;
	String s3;
	s3 = s1 + s2;
	cout << s3;
}

int main()
{
	test04();
	system("pause");
	return 0;
}
2.    虽然构造函数调用之后,对象中已经有了一个初始值,但是不能将其称作为类对象成员的初始化, 构造 函数体中的语句只能将其称作为赋初值 ,而不能称作初始化。因为 初始化只能初始化一次,而构造函数体内 可以多次赋值
#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year, int month, int day)
	{
		this->m_year = year;
		this->m_month = month;
		this->m_day = day;
	}

private:
	int m_year;
	int m_month;
	int m_day;
};

3.初始化列表

1. 每个成员变量在初始化列表中 只能出现一次 ( 初始化只能初始化一次 )
2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
引用成员变量
const 成员变量
自定义类型成员 ( 该类没有默认构造函数 )
3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使 用初始化列表初始化。
#include<iostream>
using namespace std;

class Time
{
public:
	Time(int h=0 ) :hour(h) {}//有了全缺省有参构造可以不用默认构造

public:
	int hour;
};

class Date
{
public:
	Date(int day)
	{//有了全缺省有参构造可以不用默认构造对于t,也可以不用在函数体内或者初始化列表初始化
		this->day = day;
	}

public:
	Time t;
	int day;
};

int main()
{
	Date d(10);
	cout << d.t.hour << endl;

	system("pause");
	return 0;
}
#include<iostream>
using namespace std;

class Test1
{
 public:

	 Test1(int a)
	 {
		 t = a;
	 }

 private:
	 int t;
};

class Test2
{
public:

	Test2(int a,int b, const Test1& t):ref(a),ca(b),t1(t)
	{

	}

private:
	int& ref;
	const int ca;
	Test1 t1;
};

成员变量初始化顺序只与成员变量定义的顺序有关。

#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year, int month, int day) :m_month(month), m_year(year), m_day(day), ref(day),
		c_value(100)
	{

	}
private://初始化顺序只和这里的定义顺序有关,和上面的初始化列表无关
	int m_year;
	int m_month;
	int m_day;
	int& ref;//引用数据成员 必须通过参数列表进行初始化
	const int c_value;
};

int main()
{
	Date dt(2023, 2, 5);

	system("pause");
	return 0;
}

初始化顺序例题:

#include<iostream>
using namespace std;


class Test
{
public:
	Test(int a) :_a1(a), _a2(_a1)
	{

	}
	void Print()
	{
		cout << "_a1 =" << this->_a1 << " _a2 =" << this->_a2 << endl;
	}

private:
	int _a2;//先初始化a2在初始化a1
	int _a1;
};

int main()
{
	Test t(1);
	t.Print();

	system("pause");
	return 0;
}

        隐式类型转换的发生,不一定类只有一个成员变量,可以有多个(只要缺省值够多,只剩一个或者没有未缺省的值)就可以发生隐式类型转换。

       4 友元函数:友元函数可以在类外通过对象访问类的私有成员和保护成员(这个对象可以通过传参得到,也可以通过函数内部实例化得到)。友元函数可以直接访问类的私有成员和保护成员,它是定义在类外部普通函数,不属于任何类,但需要在类的内部声 明,声明时需要加friend关键字。因此友元函数没有this指针。

       注意:友元函数可访问类的私有和保护成员,但不是类的成员函数

                友元函数 不能用 const 修饰
                友元函数 可以在类定义的任何地方声明, 不受类访问限定符限制
                一个函数可以是多个类的友元函数
                友元函数的调用与普通函数的调用和原理相同
         输出运算符重载是友元函数的典型用法, 它为了实现cout在符号左边,成员符号右边必须写成友元函数实现。
#include<iostream>
using namespace std;

class A
{
friend void func(A& a);
public:

	A(int a = 0) :m_a(a) {}

private:
	int m_a;
};

void func(A& a)
{
	a.m_a = 30;
	cout << a.m_a << endl;

	A a1(50);
	a1.m_a = 60;
	cout << a1.m_a << endl;
}

int main()
{
	A a;
	func(a);

	system("pause");
	return 0;
}

一个函数可以做多个类的友元函数

#include<iostream>
using namespace std;

class B;

class A
{
friend void func(A& a, B& b);
public:
	A(int a = 0):m_a(a) {}

private:
	int m_a;
};

class B
{
friend void func(A& a, B& b);
public:
	B(int b=0):m_b(b){ }
private:
	int m_b;
};

void func(A& a, B& b)
{
	b.m_b = 20;
	a.m_a = 10;
	cout << a.m_a << " " << b.m_b << endl;
}

int main()
{
	A a;
	B b;
	func(a, b);

	system("pause");
	return 0;
}

成员函数做友元:让一个类的成员函数作为另一个类的友元函数。友元函数可以传参或者实例化目标类的对象,这个对象可以访问目标类的非公有成员。成员函数做友元会遇到类声明顺序的问题,一般是前面的类成员访问后面类的私有成员,这样就把前面的类作用域和成员函数声明过了,能够正常声明为友元函数并进行访问,反之不行,后访问前会出现后面的类未定义,不能正常声明成友元函数。

#include<iostream>
using namespace std;

class B;

class A
{
	friend void B::PrintA(A& a);
public:
	void PrintB(B& b);
	A(int a);
private:
	int m_a;
};

class B
{
friend void A:: PrintB(B& b);
public:
	B(int b);

	void PrintA(A& a);
private:
	int m_b;
};

A::A(int a = 0) : m_a(a) {}
B::B(int b = 0) : m_b(b) {}

void B::PrintA(A& a) 
{ a.m_a = 10;
cout << a.m_a << endl;
}

void A::PrintB(B& b)
{
	b.m_b = 20;
	cout << b.m_b << endl;
}

 正确代码:

#include<iostream>
using namespace std;

class B;

class A
{
//friend void B::PrintA(A& a);
public:
	void PrintB(B& b);
	A(int a);
private:
	int m_a;
};

class B
{
friend void A:: PrintB(B& b);
public:
	B(int b);

	//void PrintA(A& a);
private:
	int m_b;
};

A::A(int a = 0) : m_a(a) {}
B::B(int b = 0) : m_b(b) {}

//void B::PrintA(A& a) 
//{ a.m_a = 10;
//cout << a.m_a << endl;
//}

void A::PrintB(B& b)
{
	b.m_b = 20;
	cout << b.m_b << endl;
}

int main()
{
	A a;
	B b;
	a.PrintB(b);

	system("pause");
	return 0;
}

友元类:将一个类声明为另一个类的友元,友元类中所有的成员函数都是目标类的友元函数,可以在类外访问它的私有和保护成员。这里注意涉及到类的声明顺序,通过学习博客发现:

  • 类的声明相关资料:

        不完全类型(只声明的类)只能在非常有限的情况下使用:可以定义指向这种类型的指针或引用,也可以作为一个已经声明(但没有定义)的函数的参数或返回类型。
        对于一个类来说,在创建它的对象前必须首先完成类的定义,而不能仅仅被声明。否则编译器就无法了解这样的对象需要多少存储空间。类似的,类也必须首先被定义,然后才能用引用或者指针访问其成员。

    简而言之:

        如果在一段代码中使用了A类实例化对象(为堆区开辟对象)或者成员变量、成员函数,那么A类必须在这段代码之前定义;
        如果这段代码只使用A类来定义指针或者函数参数中的数据类型那么A类可以在这段代码上面声明,而在下面定义。
    ————————————————
    版权声明:本文为CSDN博主「拒绝省略号」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_49030008/article/details/123243230

  • #include<iostream>
    #include<vld.h>
    using namespace std;
    
    class B;
    class A
    {
    public:
    	A(int a, int b);
    	void setB(int i);
    	void getB();
    	~A();
    
    private:
    	int m_a;
    	B* pb;
    };
    
    class B
    {
    friend class A;
    public:
    	B(int b);
    
    private:
    	A* pa;
    	int m_b;
    };
    
    A::A(int a=0, int b=0):m_a(a), pb(new B(b)) {}
    A::~A() { delete pb; pb = nullptr; }
    B::B(int b = 0) :m_b(b) {}
    
    
    void A::setB(int i)
    {
    	this->pb->m_b = i;
    }
    
    void A::getB()
    {
    	cout << this->pb->m_b << endl;
    }
    
    int main()
    {
    	A a;
    	a.setB(30);
    	a.getB();
    
    system("pause");
    return 0;
    }
    
    
    
    #include<iostream>
    using namespace std;
    
    //class B;
    class A
    {
    friend class B;
    public:
    	A(int a = 0)
    	{
    		this->m_a = a;
    	}
    
    	/*void showB(const B& b)
    	{
    		cout << b.m_b << endl;
    	}*/
    
    private:
    	int m_a;
    };
    
    class B
    {
    //friend class A;
    public:
    	B(int b=0):m_b(b)
    	{
    	}
    
    	void showA(A& a)
    	{
    		cout << a.m_a << endl;
    	}
    
    private:
    	int m_b;
    };
    
    int main()
    {
    	A a(10);
    	B b;
    	b.showA(a);
    
    	system("pause");
    	return 0;
    }

1.友元关系不能被继承。

2.友元关系是单向的。

3.友元关系不具有传递性。

#include<iostream>
using namespace std;


//class B;
class A
{
friend class B;
public:
	A(int a = 0)
	{
		this->m_a = a;
	}

	/*void showB(const B& b)
	{
		cout << b.m_b << endl;
	}*/

private:
	int m_a;
};

class B
{
//friend class A;
public:
	B(int b=0):m_b(b)
	{
	}

	void showA(A& a)
	{
		cout << a.m_a << endl;
	}

private:
	int m_b;
};

int main()
{
	A a(10);
	B b;
	b.showA(a);

	system("pause");
	return 0;
}

静态成员:

静态成员函数:1.静态成员函数没有this指针,静态成员函数只能访问静态成员变量。静态成员函数也受到访问权限限定符的限制。

静态成员变量:1.在编译时分配内存。

                         2.必须在类外进行初始化。

                         3.由所有对象共享,它是属于整个类的。

                         4.可以使用对象名调用也可以使用类名调用。

                         5.静态成员也受到访问权限的限制。

#include<iostream>
using namespace std;

//普通可调常和静态 静态和常不能调普通
//Test* const this  普通方法this指针
// const Test* const this 常方法this指针
//静态成员函数 没有this指针

class Test
{
public:
	Test()//:m_value(0)//不能在类内初始化
	{
		this->m_data = 0;
		//this->m_value = 0;//不能在类内初始化
	}

	static void Show()
	{
		//cout << "m_data =" << m_data << endl;//静态成员函数只能调动静态成员
		cout << "m_value" << m_value << endl;
		//Fun();静态成员不能调普通成员
	}

	void Fun()//普通成员可以调动静态成员
	{
		this->m_data = 1;
		this->m_value = 10;
		this->Print();//普通方法可以调动常方法
		this->Show();//普通方法可以调静态方法
	}

	void Print() const
	{
		cout << "This is Print()" << endl;
		//Fun();//常方法不能调普通方法
	}

static int m_value;
private:
	int m_data;
};
//静态成员并不属于某个对象,属于整个类,所有对象共享
int Test::m_value = 0;//静态成员变量只能在类外进行初始化

void test01()
{
	Test t1;
	Test t2;
	cout << t2.m_value << endl;
	t1.m_value = 100;
	cout << t2.m_value << endl;
}

void test02()
{
	Test t1;
	t1.Fun();

	t1.Show();//类名和对象名都可以调用
	Test::Show();
}

void main()
{
	test02();
}

静态成员可以作类的实例化对象计数器:

#include<iostream>
using namespace std;

void fun2();

class Test
{
	friend void fun(const Test& t);//没有this指针
public:
	Test(int data =0)
	{
		this->m_data = data;
		this->count++;
	}

	static void ShowCount()
	{
		cout << count << endl;
	}

	~Test()
	{
		this->count--;
	}

private:
	int m_data;
	static int count;
};
int Test::count;

void test()
{
	
		Test t[100];
		Test::ShowCount();
	
		t[99].ShowCount();
	
	Test::ShowCount();
}

void main()
{
	test();
	system("pause");
}

c11规定,可以在声明类的成员变量时,给它赋默认值,即缺省参数。

#include<iostream>
using namespace std;

//友元函数可以在类外访问类的保护和私有成员
//友元函数不能用const修饰

class B
{
	friend class A;//友元类的使用
public:
	B(int b = 0) :m_b(b) {}

private:
	int m_b;
};

class A 
{
public:

	void Print()
	{
		cout << "sizeof(p)=" << sizeof(p) << endl;
		cout << "b.m_b=" << this->b.m_b<< endl;
		cout << "a.m_a=" << this->m_a << endl;
	}

private:
	int* p = (int*)malloc(sizeof(int));
	B b = 20;
	int m_a = 10;
};

int main()
{
	A a;
	a.Print();

	system("pause");
	return 0;
}

内部类,一个类的内部又定义了另一个类,主要注意在内部类实例化对象时,前面要加上外部类的作用域限定符。

#include<iostream>
using namespace std;

class Test
{
public:
	class Stu
	{
	public:
		void func()
		{
			cout << "AAA" << endl;
		}
	};

	void func()
	{
		cout << "BBB" << endl;
	}
};

int main()
{
	Test t;
	t.func();
    
	Test::Stu s;
	s.func();

	system("pause");
	return 0;
}

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

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

相关文章

【DSView逻辑分析抓取波形CAN步骤-硬件连接-数据解析-底层波形认识CAN-工具使用】

【DSView逻辑分析抓取波形CAN步骤-硬件连接-数据解析-底层波形认识CAN】1、概述2、实验环境3、写在前面的一个问题4、实验准备&#xff08;1&#xff09;硬件连接1&#xff09;CAN卡连接开发板&#xff08;2&#xff09;逻辑分析仪连接开发板&#xff08;2) CAN卡连接软件&…

Linux 文件锁 - fcntl

什么是文件锁&#xff1f; 即锁住文件&#xff0c;不让其他程序对文件做修改&#xff01; 为什么要锁住文件&#xff1f; 案例&#xff0c;有两个程序&#xff0c;都对一个文件做写入操作。 #include <unistd.h> #include <stdio.h> #include <stdlib.h> …

【集群】Slurm作业调度系统的使用

最近使用集群进行实验&#xff0c;记录并学习集群系统进行深度学习的实验过程。集群所使用的作业调度系统为Slurm&#xff0c;这里记录下使用的常用命令和一些注意事项。 Slurm简介 Slurm是一个开源&#xff0c;容错&#xff0c;高度可扩展的集群管理和作业调度系统&#xff0…

excel数据处理: 如何用99个空格提取单元格数据

脑洞大开&#xff0c;提取单元格数据用99个空格就成&#xff01;真想扒开那些大神的脑袋看看&#xff0c;是怎么想出这样匪夷所思的方法的。需要从规格型号中提取容值、封装、耐压三组数据&#xff0c;如下&#xff1a;数据源在A列&#xff0c;数据量很大&#xff0c;需要提取的…

微信小程序Springboot短视频分享系统

3.1小程序端 用户注册页面&#xff0c;输入用户的个人信息点击注册即可。 注册完成后会返回到登录页面&#xff0c;用户输入自己注册的账号密码即可登录成功 登录成功后我们可以看到有相关的视频还有视频信息&#xff0c;我的信息等。 视频信息推荐是按照点击次数进行推荐的&am…

Zabbix 构建监控告警平台(四)

Zabbix ActionZabbix Macros1.Zabbix Action 1.1动作Action简介 当某个触发器状态发生改变(如Problem、OK)&#xff0c;可以采取相应的动作&#xff0c;如&#xff1a; 执行远程命令 邮件&#xff0c;短信&#xff0c;微信告警,电话 1.2告警实验简介 1. 创建告警media type&…

9.语义HTMLVScode扩展推荐

语义HTML 定义&#xff1a; 一个元素使用我们并不是只关心他是什么样子的&#xff0c;而是要去关心这个元素名称的实际意义或者代表什么 我们使用标签并不是他仅代表导航栏&#xff0c;只是将导航栏部分归为一个块。现实生活中&#xff0c;多使用之前都是使用div这个元素去构…

删除有序数组中的重复项-力扣26-java

一、题目描述给你一个 升序排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。由于在某些语言中不能改变数组的长度&#xff0c;所以必须将结果放在数组nums…

软件设计(九)

软件设计&#xff08;八&#xff09;https://blog.csdn.net/ke1ying/article/details/128954569?spm1001.2014.3001.5501 81、模块A将学生信息&#xff0c;即学生姓名、学号、手机等放到一个结构体系中&#xff0c;传递给模块B&#xff0c;模块A和B之间的耦合类型为 什么耦合…

【C++设计模式】学习笔记(1):面向对象设计原则

目录 简介面向对象设计原则(1)依赖倒置原则(DIP)(2)开放封闭原则(OCP)(3)单一职责原则(SRP)(4)Liskov替换原则(LSP)(5)接口隔离原则(ISP)(6)优先使用对象组合,而不是类继承(7)封装变化点(8)针对接口编程,而不是针对实现编程结语简介 Hello! 非常感谢您阅读海…

变分自编码器背后的直觉【VAE】

在阅读有关机器学习的内容时&#xff0c;你遇到的大部分材料可能都与分类问题有关。 你有一个特定的输入&#xff0c;ML 模型试图找出该输入的特征。 例如&#xff0c;分类模型可以决定图像中是否包含猫。 当你想创建具有预定义特征的数据时&#xff0c;反过来又如何呢&#x…

再不跳槽,就晚了

从时间节点上来看&#xff0c;3月、4月是每年跳槽的黄金季&#xff01; 以 BAT 为代表的互联网大厂&#xff0c;无论是薪资待遇、还是平台和福利&#xff0c;都一直是求职者眼中的香饽饽&#xff0c;“大厂经历” 在国内就业环境中无异于一块金子招牌。在这金三银四的时间里&a…

预处理指令详解

预处理指令详解**1.预定义符号****2.#define****2.1 #define 定义标识符****2.2 #define 定义宏****2.3 #define 替换规则****2.4 #和##****#的作用****##的作用****2.5 带副作用的宏参数****2.6 宏和函数的对比****宏和函数对比图****2.7 命名约定****3.#undef**4.条件编译4.1…

Leg转Goh引擎和架设单机+配置登陆器教程

教程准备1、Leg版本一个2、Goh引擎一套3、电脑一台(最好联网)前言&#xff1a;BLUE/LEGS/Gob/Goh/九龍、4K、AspM2第一步&#xff1a;更换引擎1、把版本自带的LEG引擎换成Goh引擎2、删除服务端里面的exe、dll文件(也可以直接更新)3、清理登录和游戏网关里面的配置文件4、更新引…

Sandman:一款基于NTP协议的红队后门研究工具

关于Sandman Sandman是一款基于NTP的强大后门工具&#xff0c;该工具可以帮助广大研究人员在一个安全增强型网络系统中执行红队任务。 Sandman可以充当Stager使用&#xff0c;该工具利用了NTP&#xff08;一个用于计算机时间/日期同步协议&#xff09;从预定义的服务器获取并…

菌子导航系统(持续开发中)

文章目录菌子导航前言项目架构spring-cloud 和 spring-boot 版本选择使用到的组件&#xff08;依赖&#xff09;架构分层项目基本功能1 使用Nacos做配置中心2 logback日志3 mybatis-plus操作数据库4 Caffeine 缓存整合5 LocalDateTime 序列化&反序列化6 参数校验快速失败配…

ubuntu20.04 系统下 .7z 文件解压缩到指定的目录下

问题描述 环境&#xff1a; ubuntu 20.04 ubuntu 下有个 7z 的压缩文件需要解压&#xff0c;需要解压到指定的目录下&#xff0c;而不是压缩包当前目录下 安装 p7zip-full ubuntu 下的 7z 解压软件&#xff1a; p7zip-full 安装命令&#xff1a; sudo apt install p7zip-fu…

04-PS人像磨皮方法

1.高斯模糊磨皮 这种方法的原理就是建立一个将原图高斯模糊后图层, 然后用蒙版加画笔或者历史画笔工具将需要磨皮的地方涂抹出来, 通过图层透明度, 画笔流量等参数来控制磨皮程度 1.新建图层(命名为了高斯模糊磨皮), 混合模式设置为正常, 然后选择高斯模糊, 模糊数值设置到看…

前端也能悄悄对视频截图?js实现对视频按帧缓存

前言 虽然最后没有采用这种方案来实现滚动控制视频进度&#xff0c;但是仍然想自己试试这种方案的实现&#xff0c;毕竟应用范围也挺广的。 核心代码并不多&#xff0c;算是一篇小短文&#xff5e;。 掘金好像不允许放站外演示链接&#xff0c;所以这里就用动图大概展示下最终…

STL——list

一、list介绍及使用 1. list文档介绍 &#xff08;1&#xff09;list是可以在常数范围内&#xff0c;在任意位置进行插入、删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 &#xff08;2&#xff09;list的底层是带头结点的双向循环链表&#xff0c;其中每个元素…