C++ ------ 类和对象的深究

文章目录

  • 构造函数
    • 初始化列表
      • 概念
      • 特性
    • explicit关键字
  • static成员
    • 概念
    • 特点
  • 友元
    • 友元函数
    • 友元类
      • 概念
      • 特性
  • 内部类
    • 概念
    • 特点
  • 匿名对象
  • 拷贝对象时的一些编译器优化

构造函数

我们来看下面的代码:

#include <iostream>
using namespace std;class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};

上面的代码虽然在每次实例化对象是都给每个成员变量设定了初始值,但这并不是初始化,构造函数体内的语句只能将其称为赋初值,并不是初始化,因为初始化成员变量只能初始化一次,而构造函数可以给成员变量多次赋值,所以怎么样才能对成员变量初始化呢,这里引出了初始化列表的概念

初始化列表

概念

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个“成员变量”后面跟一个放在括号中的初始值或表达式。

class Date
{
public:Date(int year, int month, int day):_year(year),_month(month),_day(day){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};
int main()
{Date d(1, 1, 1);return 0;
}

在这里插入图片描述

特性

  • 每个成员变量在初始化类表中最多只能出现一次(初始化只能初始化一次)
  • 类中包含以下成员,必须放在初始化列表位置进行初始化
  • 引用成员变量
  • const成员变量
  • 自定义类型成员(且该类没有默认构造函数时)
class A
{
public:A(int a):_a(a){std::cout << "A(int a)" << std::endl;}
private:int _a;
};class B
{B(int a, int& b):_b(a),_c(b),obj(2){std::cout << "B(int a, int& b)" << std::endl;}
private://const和引用的特点是必须在声明时初始化const int _b;int& _c;//自定义类型A obj;
};

这里需要注意:如果这里A对象为默认的无参构造函数,则B的初始化列表就不能对obj进行初始化,obj对象只能由编译器调用A类的无参的默认构造函数来创建对象。如果想要在B的初始化列表初始化我们必须把A的默认构造写成缺省构造。如以下代码:

class A
{
public:A(int a = 1){std::cout << "A(int a = 1)" << std::endl;}
private:int _a;
};class B
{
public:B(int a, int& b):_b(a), _c(b),obj(10){std::cout << "B(int a, int& b)" << std::endl;}
private://const和引用的特点是必须在声明时初始化const int _b;int& _c;//自定义类型A obj;
};int main()
{int n = 10;B b1(1,n);
}

当然也可以让编译器自动调用该缺省默认函数

class A
{
public:A(int a = 1):_a(a){std::cout << "A(int a = 1)" << std::endl;}
private:int _a;
};class B
{
public:B(int a, int& b):_b(a), _c(b){std::cout << "B(int a, int& b)" << std::endl;}
private://const和引用的特点是必须在声明时初始化const int _b;int& _c;//自定义类型A obj;
};int main()
{int n = 10;B b1(1,n);
}

在这里插入图片描述

我们书写构造函数时尽量写成初始化列表的构造函数,但是初始化列表代替不了函数体内赋值,比如:

template <typename T1>
//模板类
class Stack
{
public:Stack(int capacity = 3):_array((T1*)malloc(sizeof(T1)*capacity)),_capacity(capacity),_top(0){//对malloc进行判空if (NULL == _array){ferror("malloc failed!");return;}}void CheckCapacity(){if (_top == _capacity){T1* temp = (T1*)realloc(_array, sizeof(T1) * _capacity * 2);if (NULL == temp){perror("realloc failed!\n");return;}_array = temp;_capacity *= 2;cout << "扩容成功!" << endl;}}void PushStack(T1 x){CheckCapacity();_array[_top] = x;_top++;}void PopStack(){if (_top > 0){_top--;}else{cout << "退栈失败,栈已为空!" << endl;return;}}bool EmptyStack(){return _top == 0;}T1 StackTop(){return _array[_top - 1];}~Stack(){delete[] _array;_array = NULL;_capacity = _top = 0;}
private:T1* _array;int _capacity;int _top;
//我们还可以在外面控制capacity比如用两个栈实现一个队列
class MyQueue
{
public:MyQueue(){}MyQueue(int capacity):pushst(capacity),popst(capacity){}
private:Stack<int> pushst;Stack<int> popst;
};int main()
{MyQueue q1(10);
}
};

这里还有一个坑,声明顺序和定义的顺序保持一致。 我们来看一下下面代码:

class A
{
public:A(int a):_a1(a)   //_a1 = 1,_a2(_a1) //_a2 = 随机值{}void Print(){cout << _a1 << " " << _a2 << endl;}
private:int _a2;int _a1;
};int main()
{A aa(1);aa.Print();return 0;
}

在这里插入图片描述
所以我们需要在初始化列表中初始的顺序要和声明的顺序相同。

explicit关键字

构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。

class Date
{//友元函数friend ostream& operator<<(ostream& cout, const Date& d);
public:Date(int year):_year(year){}/*Date(int year, int month = 1, int day = 1):_year(year),_month(month),_day(day){}*///赋值运算符重载Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& cout, const Date& d)
{cout << d._year << "-" << d._month << "-" << d._day << endl;return cout;
}int main()
{Date d1(2022);//2023利用构造函数生成一个Date类型的对象然后再利用赋值运算符重载函数赋值给d1//但是编译器会默认优化为一个构造d1 = 2023;cout << d1 << endl;
}

单参构造函数,没有使用explicit修饰,具有类型转换的作用。
explicit 修饰构造函数,禁止类型转换
缺省构造函数,虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具有类型转换作用。explicit修饰构造函数,禁止类型转换。
以上main函数里的代码是:利用一个整型变量给日期类型对象赋值,实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值。

class Date
{//友元函数friend ostream& operator<<(ostream& cout, const Date& d);
public:explicit Date(int year):_year(year){}/*Date(int year, int month = 1, int day = 1):_year(year),_month(month),_day(day){}*///赋值运算符重载Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& cout, const Date& d)
{cout << d._year << "-" << d._month << "-" << d._day << endl;return cout;
}int main()
{Date d1(2022);//2023利用构造函数生成一个Date类型的对象然后再利用赋值运算符重载函数赋值给d1//但是编译器会默认优化为一个构造d1 = 2023;cout << d1 << endl;
}

在这里插入图片描述

结论:用explicit修饰构造函数,将会禁止构造函数的隐式转换。

static成员

概念

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量,用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。
我们来看下面的代码:
实现一个类,计算程序中创建出了多少个类对象。

class A
{
public:A(){scount++;}A(const A& a){scount++;}~A(){scount--;}static int GetScount(){return scount;}
private:static int scount;
};int A::scount = 0;A a;
int main()
{cout << __LINE__ << " " << A::GetScount() << endl;A b = a;cout << __LINE__ << " " << A::GetScount() << endl;
}

在这里插入图片描述

特点

  • 静态成员为所有类对象所共享,不属于某个具体的对象,存放再静态区。
  • 静态成员变量必须在类外定义,定义是不添加static关键字,类中只是声明
  • 类静态成员即可用类名 :: 静态成员或者对象.静态成员来访问。
  • 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  • 静态成员也是类的成员,受public、protected、private访问限定符的限制。
    需要注意:静态成员函数可以调用静态成员函数,不能调用非静态成员函数,非静态成员函数可以调用静态成员函数。

友元

友元最基本的作用是让类外的函数可以访问类内的私有成员。但是它破化了封装性,所以我们需要谨慎使用友元,友元可以分为友元函数和友元类。
我们需要实现流插入和流提取的运算符重载,但是它们不能被实现为类成员函数,因为实现成类成员函数,会有this指针抢占第一个运算形参的位置,这就会造成冲突,this指针默认是第一个参数了。但是实际使用中cout需要是第一个形参对象才能正常使用。所以要将流插入和流提取运算符重载为全局函数。但这会导致类外的函数不能访问类内的成员,此时就需要友元来解决。看下面的代码我们实现了这两个运算符的重载。

友元函数

class Date
{//友元函数friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Date(int year = 1900,int month = 1,int day = 1):_year(year),_month(month),_day(day){}/*Date(int year, int month = 1, int day = 1):_year(year),_month(month),_day(day){}*///赋值运算符重载Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day << endl;return cout;
}
istream& operator>>(istream& _cin,Date& d) 
{_cin >> d._year;_cin >> d._month;_cin >> d._day;return cin;
}int main()
{Date d1;//2023利用构造函数生成一个Date类型的对象然后再利用赋值运算符重载函数赋值给d1//但是编译器会默认优化为一个构造cin >> d1;cout << d1 << endl;
}

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

友元类

class Time
{//date类是Time的友元。//友元只能是单向的//在该例子中不能访问Date类中的成员而Date类可以访问Time类中的成员friend class Date;
public:Time(int hour = 0, int minute = 0, int second = 0):_hour(hour),_minute(minute),_second(second){}
private:int _hour;int _minute;int _second;
};
class Date
{//友元函数friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Date(int year = 1900, int month = 1, int day = 1):_year(year), _month(month), _day(day){}/*Date(int year, int month = 1, int day = 1):_year(year),_month(month),_day(day){}*///设定日期类的时间void SetTimeofDate(int hour, int minute, int second){_t._hour = hour;_t._minute = minute;_t._second = second;}//赋值运算符重载Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;Time _t;
};
ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day << endl;return cout;
}
istream& operator>>(istream& _cin, Date& d)
{_cin >> d._year;_cin >> d._month;_cin >> d._day;return cin;
}int main()
{Date d1;//2023利用构造函数生成一个Date类型的对象然后再利用赋值运算符重载函数赋值给d1//但是编译器会默认优化为一个构造cin >> d1;cout << d1 << endl;
}

概念

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

特性

友元关系是单向的,不具有交换性。

  • 比如上述的Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量是不可以的,所以具有单项行,谁是谁的友元类,谁就能访问另一个类的私有成员。

友元关系不能传递

  • 如果C是B的友元,B是A的友元,则不能说明C是A的友元。

友元关系不能被继承。

内部类

class Date
{//友元函数
public:Date(int year = 1900, int month = 1, int day = 1):_year(year), _month(month), _day(day){}//Time天生就是Date类的友元。此类不占任何空间,也就是说sizeof(Date) == Date的大小//它受访问修饰限定符的限制,可以写成保护,私有,公共。class Time{friend class Date;public:Time(int hour = 0, int minute = 0, int second = 0):_hour(hour),_minute(minute),_second(second){}private:int _hour;int _minute;int _second;//不计算静态成员的大小static int k;};Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;
};
int Date::Time::k = 0;
int main()
{Date d1;Date::Time t;//但是不能这样写//Time t1;
}

概念

如果一个类定义在另一个类的内部,这个类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象来访问外部类中的所有成员,但是外部类不是内部类的友元。

特点

  • 内部类可以定义在外部类的public、protected、private都是可以的。
  • 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象 /类名。
class Date
{//友元函数
public:Date(int year = 1900, int month = 1, int day = 1):_year(year), _month(month), _day(day){}//Time天生就是Date类的友元。此类不占任何空间,也就是说sizeof(Date) == Date的大小//它受访问修饰限定符的限制,可以写成保护,私有,公共。class Time{friend class Date;public:Time(int hour = 0, int minute = 0, int second = 0):_hour(hour),_minute(minute),_second(second){}void foo(const Date& d){cout << k << endl;   //okcout << d.i << endl; //ok}private:int _hour;int _minute;int _second;//不计算静态成员的大小static int k;};Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;static int i;
};
int Date::Time::k = 0;
int Date::i = 0;
int main()
{Date d1;Date::Time t;t.foo(d1);//但是不能这样写//Time t1;
}

在这里插入图片描述

  • sizeof(外部类) = 外部类,和内部类没有任何关系。

在这里插入图片描述

匿名对象

匿名对象的特点是不用起名字,我们看下面的代码:

class A
{
public:A(int a = 1):_a(a){cout << "A(int a = 1)" << endl;}~A(){cout << "~A()" << endl;}private:int _a;
};int main()
{//匿名对象的特点是不用取名字,但是它们的生命周期只有这一行,我们证明这一点,它会在这一行结束后调用析构函数A();
}

在这里插入图片描述
还需要注意,引用不能引用匿名对象,但是加上const以后就可以了,这是因为匿名对象就像临时变量一样具有常性,前面加上const后就可以引用了,权限可以缩小和平移不能放大。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

拷贝对象时的一些编译器优化

class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a = aa._a;}return *this;}~A(){cout << "~A()" << endl;}
private:int _a;
};
void f1(A aa)
{}
A f2()
{A aa;return aa;
}
int main()
{// 传值传参A aa1;f1(aa1);cout << endl;// 传值返回f2();cout << endl;// 隐式类型,连续构造+拷贝构造->优化为直接构造f1(1);// 一个表达式中,连续构造+拷贝构造->优化为一个构造f1(A(2));cout << endl;// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造A aa2 = f2();cout << endl;// 一个表达式中,连续拷贝构造+赋值重载->无法优化aa1 = f2();cout << endl;return 0;
}

在这里插入图片描述
我们下一篇再见!

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

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

相关文章

MyBatis-XML映射文件

XML映射文件 规范 XML映射文件的名称与Mapper接口名称一致&#xff08;EmpMapper对应EmpMpper.xml&#xff09;&#xff0c;并且将XML映射文件和Mapper接口放置在相同包下&#xff08;同包同名&#xff09; ​​​ 在maven项目结构中所有的配置文件都在resources目录之下&…

python与深度学习(十五):CNN和宝可梦模型

目录 1. 说明2. 宝可梦模型2.1 导入相关库2.2 建立模型2.3 模型编译2.4 数据生成器2.5 模型训练2.6 模型保存 3. 宝可梦的CNN模型可视化结果图4. 完整代码5. 宝可梦的迁移学习 1. 说明 本篇文章是CNN的另外一个例子&#xff0c;宝可梦模型&#xff0c;是自制数据集的例子。之前…

面向视频会议场景的 H.266/VVC 码率控制算法研究

文章目录 面向视频会议场景的 H.266/VVC 码率控制算法研究个人总结摘要为什么要码率控制码率控制的关键会议类视频码率控制研究背景视频会议系统研究现状目前基于 R-λ模型的码率控制算法的问题文章主要两大优化算法优化算法1&#xff1a;基于视频内容相关特征值的码率控制算法…

C# 图表控件库 ScottPlot

推荐使用ScottPlot原因&#xff1a; 1.图形界面简洁&#xff0c;样式丰富 2.代码较少 3.官方提供多种实例源码&#xff0c;并可以直接通过图形界面查看&#xff0c;便于快速开发 Github源码链接&#xff1a;https://github.com/ScottPlot/ScottPlot 官网WindowFrom Demo实例…

安防视频监控汇聚平台EasyCVR接入Ehome告警,公网快照不显示是什么原因?

智能视频监控汇聚平台TSINGSEE青犀视频EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;视频监控管理平台…

小米平板6将推14英寸版!与MIX Fold 3同步推出

今天&#xff0c;知名数码博主数码闲聊站爆料消息&#xff0c;称小米平板6将推出一款Max版本&#xff0c;预计与小米MIX Fold 3同步推出。 据介绍&#xff0c;小米平板6 Max将是小米首款14英寸大屏的旗舰平板&#xff0c;平板搭载骁龙8处理器&#xff0c;在性能释放、影音表现、…

LabVIEW深度相机与三维定位实战(下)

‍‍&#x1f3e1;博客主页&#xff1a; virobotics的CSDN博客&#xff1a;LabVIEW深度学习、人工智能博主 &#x1f384;所属专栏&#xff1a;『LabVIEW深度学习实战』 &#x1f37b;上期文章&#xff1a;『LabVIEW深度相机与三维定位实战&#xff08;上&#xff09;』 &#…

计算机视觉:替换万物Inpaint Anything

目录 1 Inpaint Anything介绍 1.1 为什么我们需要Inpaint Anything 1.2 Inpaint Anything工作原理 1.3 Inpaint Anything的功能是什么 1.4 Segment Anything模型&#xff08;SAM&#xff09; 1.5 Inpaint Anything 1.5.1 移除任何物体 1.5.2 填充任意内容 1.5.3 替换任…

国内GitHub加速访问工具-Fetch GitHub Hosts

一、工具介绍 Fetch GitHub Hosts是一款开源跨平台的国内GitHub加速访问工具&#xff0c;主要为解决研究及学习人员访问 Github 过慢或其他问题而提供的 Github Hosts 同步工具。 项目原理&#xff1a;是通过部署此项目本身的服务器来获取 github.com 的 hosts&#xff0c;而…

el-table点击表格某一行添加到URL参数,访问带参URL加载表格内容并滚动到选中行位置 [Vue3] [Element-plus 2.3]

写在最前 需求&#xff1a;有个表格列出了一些行数据&#xff0c;每个行数据点击后会加载出对应的详细数据&#xff0c;想要在点击了某一行后&#xff0c;能够将该点击反应到URL中&#xff0c;这样我复制这个URL发给其他人&#xff0c;他们打开时也能看到同样的行数据。 url会根…

铰接式车辆的横向动力学仿真提供车辆模型研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

WAF绕过-AWVS+Xray+Goby+sqlmap-绕过宝塔防火墙

WAF绕过主要集中在信息收集&#xff0c;漏洞发现&#xff0c;漏洞利用&#xff0c;权限控制四个阶段。 1、什么是WAF&#xff1f; Web Application Firewall&#xff08;web应用防火墙&#xff09;&#xff0c;一种公认的说法是“web应用防火墙通过执行一系列针对HTTP/HTTPS的安…

Typescript中的元组与数组的区别

Typescript中的元组与数组的区别 元组可以应用在经纬度这样明确固定长度和类型的场景下 //元组和数组类似&#xff0c;但是类型注解时会不一样//元组赋值的类型、位置、个数需要和定义的类型、位置、个数完全一致&#xff0c;不然会报错。 // 数组 某个位置的值可以是注解中的…

数学知识(二)

一、裴蜀定理 对于任意整数a,b&#xff0c;一定存在非零整数x,y&#xff0c;使得 ax by gcd(a,b) #include<iostream> #include<algorithm>using namespace std;int exgcd(int a,int b,int &x,int &y) {if(!b){x 1,y 0;return a;}int d exgcd(b,a %…

操作系统知识点总结

操作系统知识点总结: 第一章:操作系统概述 1.1操作系统的概念: ​ 操作系统是一种系统软件,与其他系统软件和应用软件不同,它有自己的基本特征。它的四大基本特征也就是并发,共享,虚拟,异步。 1.2操作系统的特征(四大基本特征): 并发: 这里我们要理解什么是并发,什么是…

Centos7 上安装 redis-dump 和redis-load 命令

一、安装rvm 1、安装GPG keys gpg2 --keyserver keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDBcurl -sSL http://rvm.io/mpapis.asc | gpg2 --import - curl -sSL http://rvm.io/pkuczynski.asc | g…

No primary or single unique constructor found for interface java.util.List

报错截图&#xff1a; 报错内容&#xff1a; 2023-08-04 15:46:32.884 ERROR 14260 --- [io-8080-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing fa…

企业级开发中协同开发与持续集成持续部署

文章目录 1 创建代码仓库2 使用git协同开发2.1 独立团队开发2.2 多团队开发git工作流 2 持续集成和持续部署2.1 创建docker镜像2.2 使用coding构建 1 创建代码仓库 每个项目有唯一的代码仓库&#xff0c;所以不是每个开发者都需要创建一个代码仓库&#xff0c;一般都是项目负责…

【C#学习笔记】内存管理

文章目录 分配内存释放内存GC标记清除算法分代算法 .NET的GC机制有这样两个问题&#xff1a; 官方文档 自动内存管理 自动内存管理是CLR在托管执行过程中提供的服务之一。 公共语言运行时的垃圾回收器为应用程序管理内存的分配和释放。 对开发人员而言&#xff0c;这就意味着…

《Web安全基础》03. SQL 注入

web 1&#xff1a;简要 SQL 注入2&#xff1a;MySQL 注入2.1&#xff1a;信息获取2.2&#xff1a;跨库攻击2.3&#xff1a;文件读写2.4&#xff1a;常见防护 3&#xff1a;注入方法3.1&#xff1a;类型方法明确3.2&#xff1a;盲注3.3&#xff1a;编码3.4&#xff1a;二次注入3…