【C++】类和对象——默认成员函数(下)

目录

  • 前言
  • 拷贝构造
    • 1.概念
    • 2.特征
    • 3.总结
  • 赋值重载
    • 运算符重载
    • 赋值运算符重载
      • 探讨传引用返回和传值返回的区别
  • const成员
  • 取地址及const取地址操作符重载

请添加图片描述

前言

  上一讲我们已经说了关于C++的默认成员函数中的两个——构造和析构函数。所谓默认成员函数也就是:用户没有显示定义实现时,编译器会自动生成的成员函数。
6个默认成员函数如下:

  1. 构造函数(主要完成初始化操作)
  2. 析构函数(主要完成清理操作)
  3. 拷贝构造(使用同类对象初始化对象)
  4. 赋值重载(把一个对象赋值给另一个对象)
  5. 取地址重载
  6. const取地址操作符重载

  下面我们来介绍剩下四个默认成员函数。

拷贝构造

1.概念

  只有单个形参,该形参是对本类对象类型对象的引用(一般用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

class Date
{
public:Date(int year, int month, int day)//用初始化列表做构造函数:_year(year),_month(month),_day(day){}Date(const Date& d)//拷贝构造函数,不写编译器会默认生成{_year = d._year;_month = d._month;_day = d._day;}private:int _year = 2024;int _month = 7;int _day = 15;
};int main()
{Date d1(2024, 7, 15);Date d2(d1);Date d3 = d1;//拷贝构造的形式return 0;
}

Date d2(d1);
Date d3 = d1;
是拷贝构造的两种形式,都是用同类型对象拷贝初始化

为了防止有人在写拷贝构造函数时会将其写成:
Date(Date& d)
{
d._year = _year;
d._month = _month;
d._day = _day;
}
所以我们在传入参数时增加const,使其不被修改,也就变成了:Date(const Date& d)

2.特征

拷贝构造函数也是特殊的成员函数,也包括一些独有的特性。

  1. 拷贝构造函数是构造函数的一种重载形式,所以拷贝构造函数是构造函数的一种特殊的形式,也拥有构造函数的特性

  1. 拷贝构造函数的参数只有一个且必须是类类型对象的引用使用传值方式编译器会报错,因为它会引发无穷递归
      下面我们来验证一下为何一定要用带引用的参数。
    首先我们要明白一个点,函数调用时需要传参,内置类型直接传参,但是对于自定义类型需要调用拷贝构造才能完成
    在这里插入图片描述
    所以当你调用拷贝构造没有用引用传参时,编译器会一直调用拷贝构造函数,形成无限递归,当然现在编译器也会通过报错来告诉你不能直接传类类型的参数,如下:
    在这里插入图片描述

  1. 若未显示定义,编译器会生成默认的拷贝构造函数,默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫浅拷贝,也叫值拷贝

注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式拷贝的,而自定义类型是调用其拷贝构造函数完成的。

  1. 编译器生成的默认拷贝构造已经可以完成字节序的值拷贝了,那还需要显示定义吗,我们来看看Stack栈这个类:
typedef int DataType;
class Stack
{
public:Stack(int capacity = 10){_arr = (DataType*)malloc(capacity * sizeof(DataType));if (_arr == nullptr){perror("malloc申请空间失败");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){_arr[_size++] = data;}~Stack(){if (_arr){free(_arr);_arr = nullptr;_size = 0;_capacity = 0;}}private:DataType* _arr;int _size;int _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2(s1);return 0;
}

  在以上代码中,我们简单写了一个栈,我们没有显示写拷贝构造函数,编译器会默认生成,但是我们发现编译器并不能通过该程序,会报一个错误:
在这里插入图片描述
  出现这个错误的原因也比较多,但是根据我们的代码,问题应该出现在创建对象时的内存分配问题上,也就是s2这个对象和s1这个对象两个内存是同一个位置,在后面析构时会出错,下面我们来具体介绍了解一下。
在这里插入图片描述
  我们通过调试发现,s1和s2的地址是一样的,它们指向的是同一块空间
在这里插入图片描述
  s1对象调用构造函数创建,且放入了4个元素1 2 3 4。
  s2对象使用s1进行拷贝构造,而Stack类没有显示定义拷贝构造函数,则编译器自动生成默认的拷贝构造,是值拷贝,也就是将s1中的内容原封不动拷贝到s2中,因此s1和s2指向同一块空间
  当程序退出时,会调用析构函数对内存进行清理,即s2和s1要消耗,先构造的后析构销毁,因此s2先销毁,此时它已经将指向的内存空间释放了,但s1并不知道,s1会继续销毁,此时会将同一块空间再次释放,一块空间多次释放,就会导致程序崩溃
  因此我们对于栈这类有资源申请的类,需要自己显示定义拷贝构造函数,如下:

typedef int DataType;
class Stack
{
public:Stack(int capacity = 10){_arr = (DataType*)malloc(capacity * sizeof(DataType));if (_arr == nullptr){perror("malloc申请空间失败");return;}_size = 0;_capacity = capacity;}Stack(const Stack& st)//显示定义拷贝构造函数{_arr = (DataType*)malloc(st._capacity * sizeof(DataType));if (_arr == nullptr){perror("malloc申请空间失败");return;}memcpy(_arr, st._arr, st._size * sizeof(DataType));_size = st._size;_capacity = st._capacity;}void Push(const DataType& data){_arr[_size++] = data;}bool Empty(){return _size == 0;}DataType Top(){return _arr[_size - 1];}void Pop(){--_size;}~Stack(){if (_arr){free(_arr);_arr = nullptr;_size = 0;_capacity = 0;}}private:DataType* _arr;int _size;int _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2(s1);s2.Push(5);s2.Push(6);cout << "拷贝后:" << endl;cout << "s2:";while (!s2.Empty()){cout << s2.Top() << " ";s2.Pop();}cout << endl;cout << "s1:";while (!s1.Empty()){cout << s1.Top() << " ";s1.Pop();}cout << endl;return 0;
}

结果如下:
在这里插入图片描述
  我们发现,对栈s2进行插入元素,也没有改变s1,因为此时两者的地址不同,也就互不影响,这也叫深拷贝。

注意:类中如果没有涉及资源申请时,拷贝构造函数写不写都可以,但如果涉及资源申请,就必须要写拷贝构造函数,否则就是浅拷贝,编译器会报错。

补充一小点:
  为了提高程序效率,一般进行对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用用引用。

3.总结

对于拷贝构造函数的总结如下:

  1. 如果没有资源管理,一般情况下不需要写拷贝构造,使用默认生成的拷贝构造就可以,如:Date类。
  2. 如果都是自定义类型成员,内置类型没有指向资源,也用默认生成的拷贝构造就可以,例如:MyQueue。
  3. 一般情况下,不需要显示写析构函数,就不需要写拷贝构造。
  4. 如果内部有指针或者有指向资源的类型,需要显示写析构函数,通常要显示写构造完成深拷贝,如Stack,Queue,List等。

赋值重载

运算符重载

  我们都知道类型分为两种,一种是内置类型,一种是自定义类型,编译器对于内置类型的各种运算是了如指掌的,但是它不知道自定义类型要如何加减乘除,此时我们就引入了运算符重载,它是一个具有特殊函数名的函数。
->函数名为:关键字operator后面接需要重载的运算符符号,如operator+。
->函数形式:返回值类型 operator操作符(参数列表)
使用运算符重载时有几个需要注意的点:

  • 不能通过连接其他符号来创建新的操作符,如operator@,这是错误的。
  • 重载运算符必须有一个类类型的参数,如int operator+(int i, int j);这也是错误的。
  • 操作符是几目则参数个数就有几个,如+ 是双目操作符,参数个数就有两个,++是单目操作符,参数个数就是一个。
  • 有五个运算符是不能进行重载的:.*(调用函数成员指针)、::(域作用限定符)、sizeof?:(三目操作符)、.(对象.成员)这五个。
    下面我们来尝试写写operator==这个函数:
class Date
{
public:
//private:int _year = 2024;int _month = 7;int _day = 18;
};bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}void Test()
{Date d3(2024, 3, 10);Date d4(2024, 7, 18);cout << (d3 == d4) << endl;cout << operator==(d3, d4) << endl;
}

  对于运算符重载函数,既可以直接写出来判断如:(d3 == d4) ,也可以直接显示调用:operator==(d1,d2)。如果直接写出来,编译器也会转换成operator==(d1,d2),从底层汇编来讲二者是一样如,如下面的反汇编码所示:
在这里插入图片描述
  我们发现,我们将operator==重载成了全局函数,但是这样又有问题出现了,我们发现只有将私有成员函数将其变为公有,才能够使用,那这就破坏了代码的一个封装性。那么该如何解决呢,有以下几个办法可以解决:

  • 提供这些成员的get和set函数
    例如:
class Date
{
public:int GetYear() const{return _year;}int GetMonth() const{return _month;}int GetDay() const{return _day;}
private:int _year = 2024;int _month = 7;int _day = 18;
};bool operator==(const Date& d1, const Date& d2)
{return d1.GetYear() == d2.GetYear()&& d1.GetMonth() == d2.GetMonth()&& d1.GetDay() == d2.GetDay();
}void Test()
{Date d3(2024, 3, 10);Date d4(2024, 7, 18);cout << (d3 == d4) << endl;
}

  此时同样可以调用,至于函数后面加的const我们下面会再讲解。


  • 写成友元函数(后面会讲到)
  • 重载成成员函数
    重载成成员函数时需要注意的是不能直接将其放入到成员函数中,是因为成员函数的参数有隐含的this指针,直接放入会导致参数过多而报错,因此我们要减少参数再放入,如下所示:
class Date
{
public:bool operator==(const Date& d1){return _year == d1._year&& _month == d1._month&& _day == d1._day;}private:int _year = 2024;int _month = 7;int _day = 18;
};void Test()
{Date d3(2024, 3, 10);Date d4(2024, 7, 18);cout << (d3 == d4) << endl;
}

  但是要注意的是,当我们将其重载成了成员函数,就不能显示调用了,因为参数过多,会报错。


赋值运算符重载

  上面说的是运算符重载,现在我们来说运算符重载中的一个特例:赋值运算符重载,它是六个默认成员函数中的第四个,它是把一个已经存在的对象赋值给另一个已经存在的对象,我们来详细剖析这个函数。

  1. 赋值运算符的重载格式。
  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T& ,返回引用可以提高返回效率,有返回值的目的是为了支持连续赋值。
  • 检测是否自己给自己赋值
  • 返回*this,要符合连续赋值的含义。

当我们写一个Date类,显示定义赋值运算符重载:

class Date
{
public:Date(int year, int month, int day)//用初始化列表做构造函数:_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}Date(const Date& d)//拷贝构造函数,不写编译器会默认生成{_year = d._year;_month = d._month;_day = d._day;cout << "Date(const Date& d)" << endl;}Date& operator=(const Date& d)//显示定义赋值运算符重载{if (this != &d)//判断this指向的地址和d的地址是否相同,相同则表明是自己给自己赋值//加了判断条件就能防止自己给自己赋值,避免更多的损耗。{_year = d._year;_month = d._month;_day = d._day;}return *this;}private:int _year = 2024;int _month = 7;int _day = 15;
};int main()
{Date d1(2024, 7, 15);Date d2(2024, 7, 18);Date d3(2024, 7, 20);d1 = d2 = d3;//一个已经存在的对象赋值给另一个已经存在的对象
//此时我们将d3的日期值先赋值给了d2,然后又赋值给了d1.return 0;
}

如果是下面这段代码:
Date d4 = d1;
则这个是拷贝构造,因为这是一个已经存在的对象,拷贝给另一个要创建初始化的对象。
要注意区分二者。

  当我们定义的赋值重载函数没有返回值类型时则不能支持连续赋值,如下所示:

	void operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}}

在这里插入图片描述

探讨传引用返回和传值返回的区别

  我们再来考虑第二点,传引用返回和不传引用返回的区别是什么,什么时候可以用传引用返回,什么时候不可以。
  当我们使用引用返回时:
在这里插入图片描述
  可以看到,编译器只对三个对象进行了构造,而没有调用拷贝构造。
  但是当使用传值返回时:
在这里插入图片描述
  编译器不仅对三个变量进行了构造,还在赋值时进行了两次拷贝构造,由此我们可以看出,使用引用返回可以减少拷贝构造次数,减少消耗。
  那我们什么时候能用引用返回呢,我们来举个例子就知道了:

Date func()
{Date d(2024, 7, 20);return d;
}int main()
{const Date& ref = func();ref.Print();return 0;
}

  我们定义了一个func函数,传值返回,并将结果打印出来,如下:
在这里插入图片描述

由结果可以看出,它先通过构造函数构造了对象d,接收值打印后进行了析构,析构的是对象d。
但实际上,编译器在进行传值返回时,会将d进行一次拷贝构造出来一个临时对象,这个临时对象具有常性,ref引用的就是这个临时对象,所以需要加const。
在这里插入图片描述
但是由于编译器的优化,将构造和拷贝构造直接优化成了只剩构造,所以我们没能看出来有拷贝构造的存在,如果我们使用的编译器是vs2019,则可以看得出来。

  但是如果我们使用引用返回:
在这里插入图片描述
  我们发现,得出的结果并不是我们想要的结果,是因为返回对象是一个局部的临时对象,ref和d的地址是一样的,出了func的作用域,d就析构销毁了,所以当我们打印ref时得到的值会是随机值。
  而在原来的代码中,我们返回的是this,用引用返回,是因为this在main作用域中就创建出来了,出了main才会被销毁,而出operator=这个函数是不会被销毁的,因此可以用传引用返回。

总结来说
返回对象是一个局部对象或临时对象时,出了函数作用域会被析构销毁的话,则不能用引用返回。引用返回可以减少拷贝,但是引用返回存在风险,要看出了函数作用且对象还在,则可以使用引用返回。


  1. 赋值运算符不能重载成全局函数。

  当我们想把赋值运算符重载成全局函数时,全局函数不存在this指针,则需要用两个参数传参,如下所示:

class Date
{
public:Date(int year, int month, int day)//用初始化列表做构造函数:_year(year), _month(month), _day(day){}
//private:int _year = 2024;int _month = 7;int _day = 15;
};
Date& operator=(Date& left, Date& d)
{if (&left != &d){left._year = d._year;left._month = d._month;left._day = d._day;}return left;
}

  当我们将其重载成全局函数时,需要考虑私有成员变量的时候,这里为了方便演示,将其变成了公有。此时编译器会报错:
error C2801: “operator =”必须是非静态成员。
  原因也很简单:赋值运算符如果不显示实现,编译器会生成一个默认的,此时用户在类外自己实现的这个全局的赋值运算符重载就会和编译器在类中生成的默认赋值运算符重载冲突,所以赋值运算符重载只能是类的成员函数。


  1. 用户没有显示实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字拷贝

  值得注意的是:内置类型成员变量是可以直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。这点与拷贝构造是类似的。

const成员

  将const修饰的成员函数称为const成员函数,const修饰类成员函数,实际修饰的是该成员函数隐含的this指针,表明在该函数成员中不能对类的任何成员进行修改。
  编译器对const成员函数的处理如下:
在这里插入图片描述
  对于const,就会存在一个权限的问题,要如何使用才不会报错:
在这里插入图片描述
最后还有几个可以思考的问题:

  • const对象可以调用非const成员函数吗?
    ->不可以,这属于权限放大
  • 非const对象可以调用const成员函数吗?
    ->可以,这属于权限缩小
  • const成员函数内可以调用其他非const成员函数吗?
    ->不可以,这属于权限放大
  • 非const成员函数内可以调用其他const成员函数吗?
    ->可以,这属于权限缩小

取地址及const取地址操作符重载

  这是最后两个默认成员函数,一般不用重新定义,编译器会默认生成,我们在实践中用的也比较少。
  使用如下所示:

class Date
{
public:Date(int year, int month, int day)//用初始化列表做构造函数:_year(year), _month(month), _day(day){}Date* operator&(){return this;}const Date* operator&() const{return this;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 7, 15);const Date d2(2024, 7, 20);cout << &d1 << endl;cout << &d2 << endl;return 0;
}

  结果则会生成这两个对象的地址。
  这两个运算符一般不需要重载,使用编译器生成的默认取地址重载即可,只有特殊情况才需要重载,如想让别人获取到指定的内容。

  今天的内容到此结束啦,感谢大家观看,如果大家喜欢,希望大家一键三连支持一下,如有表述不正确,也欢迎大家批评指正。

请添加图片描述

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

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

相关文章

你的Type-c接口有几颗牙齿

C 口为啥不能混用 想想 C 口当年推出时给我们画的饼&#xff0c;“正反都能插&#xff0c;而且充电、传数据、连显示器等等&#xff0c;什么活都能干”&#xff0c;而实现这一切的前提全靠 C 口里面的 24 根针脚 这 24 根真叫呈中心对称分布&#xff0c;这种设计使得插头可以以…

iPhone手机上备忘录怎么设置字数显示

在日常生活和工作中&#xff0c;我经常会使用iPhone的备忘录功能来记录一些重要的想法、待办事项或临时笔记。备忘录的便捷性让我可以随时捕捉灵感&#xff0c;但有时候&#xff0c;我也会苦恼于不知道自己记录了多少内容&#xff0c;尤其是在需要控制字数的时候。 想象一下&a…

机器学习 | 深入理解激活函数

什么是激活函数&#xff1f; 在人工神经网络中&#xff0c;节点的激活函数定义了该节点或神经元对于给定输入或一组输入的输出。然后&#xff0c;将此输出用作下一个节点的输入&#xff0c;依此类推&#xff0c;直到找到原始问题的所需解决方案。 它将结果值映射到所需的范围…

【功能】DOTween动画插件使用

一、下载安装DOTween插件&#xff0c;下载地址&#xff1a;DOTween - Asset Store (unity.com) 使用 Free免费版本即可&#xff0c;导入成功后&#xff0c;Project视图中会出现 DOTween 文件夹 二、使用案例 需求1&#xff1a;控制材质球中的某个属性值&#xff0c;实现美术需…

SQL执行流程、SQL执行计划、SQL优化

select查询语句 select查询语句中join连接是如何工作的&#xff1f; 1、INNER JOIN 返回两个表中的匹配行。 2、LEFT JOIN 返回左表中的所有记录以及右表中的匹配记录。 3、RIGHT JOIN 返回右表中的所有记录以及左表中的匹配记录。 4、FULL OUTER JOIN 返回左侧或右侧表中有匹…

二维码如何用来存储图片?扫码看图有哪些好处

现在通过二维码来分享图片是一种很常见的方法&#xff0c;二维码可以承载大量的图片内容&#xff0c;从而节省对图片空间容量的占用&#xff0c;并且将图片放入二维码中便于分享让图片传递变得更加方便快捷&#xff0c;那么图片生成二维码具体该怎么操作呢&#xff1f;通过下面…

MySQL----初始数据类型

前言 一、tinyint 范围&#xff1a;-128-----127 在MySQL中&#xff0c;整型可以指定是有符号的和无符号的&#xff0c;默认是有符号的。可以通过UNSIGNED来说明某个字段是无符号的。如果我们向mysqlt特定的类型中插入不合法的数据&#xff0c;Mysq一般会直接拦截&#xff0c…

云计算实训06——find、stat、touch、tree、scp、crontab指令相关应用

一、find命令 1.find的作用&#xff1a;对文件进行搜索 2. 基本语法&#xff1a; find [文件路径] [选项 选项的值 ] 3.常见的选项 -name 根据文件的名称搜索文件&#xff0c;支持通配符 * -type f 代表普通文件&#xff0c;-type d 代表目录 4.* 通配符 在 linux 系统…

多多OJ评测系统 前端页面通用布局开发与优化 调整布局

目录 我们重新布局样式 个人习惯写一个最外层的样式 Header Content Footer 布局出来了 加上标签和容器 绑定样式 我们设置一些样式 页面展示效果 我们加入导航栏 搜索组件 我们这边把导航菜单抽象成一个公共的组件 引入好 页面中成功进行了展示 我们可以把这边替…

MySQL添加索引时会锁表吗?

目录 简介Online DDL概念Online DDL用法总结 简介 在MySQL5.5以及之前的版本&#xff0c;通常更改数据表结构操作&#xff08;DDL&#xff09;会阻塞对表数据的增删改操作&#xff08;DML&#xff09;。 MySQL5.6提供Online DDL之后可支持DDL与DML操作同时执行&#xff0c;降低…

【Vue】深入了解 Axios 在 Vue 中的使用:从基本操作到高级用法的全面指南

文章目录 一、Axios 简介与安装1. 什么是 Axios&#xff1f;2. 安装 Axios 二、在 Vue 组件中使用 Axios1. 发送 GET 请求2. 发送 POST 请求 三、Axios 拦截器1. 请求拦截器2. 响应拦截器 四、错误处理五、与 Vuex 结合使用1. 在 Vuex 中定义 actions2. 在组件中调用 Vuex acti…

免费【2024】springboot OA公文发文管理系统

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

js 实现扫雷游戏,源码开放,支持npm引入使用

本人开发的js版本扫雷游戏 体验地址 | Github Minesweeper game Sponsors Install and use npm i minesweeper-gameimport {Map} from minesweeper-game;const map new Map();Reset Map map.reset();TS Statement interface IMapOptions {width?: number; // Map sizeh…

数据实时获取方案之Flink CDC

目录 一、方案描述二、Flink CDC1.1 什么是CDC1.2 什么是Flink CDC1.3 其它CDC1.4 FlinkCDC所支持的数据库情况 二、使用Pipeline连接器实时获取数据2.1 环境介绍2.2 相关版本信息2.3 详细步骤2.3.1 实时获取MySQL数据并发送到Kafka2.3.2 实时获取MySQL数据并同步到Doris数据库…

初识C++|模板初阶

&#x1f36c; mooridy-CSDN博客 &#x1f9c1;C专栏&#xff08;更新中&#xff01;&#xff09; 目录 &#x1f349;1. 泛型编程 &#x1f349;2. 函数模板 &#x1f95d;2.1 函数模板概念 &#x1f95d;2.2 函数模板格式 &#x1f95d;2.3 函数模板的原理 &#x1f95…

万界星空科技QMS系统:全面赋能企业质量管理的创新引擎

万界星空科技质量管理QMS系统&#xff08;Quality Management System&#xff09;是一套全面、高效的质量管理工具&#xff0c;旨在帮助企业提升产品质量、优化生产流程、降低质量成本。该系统集成了多个功能模块&#xff0c;以满足企业在质量管理方面的各种需求。以下是万界星…

【网络安全科普】勒索病毒 防护指南

勒索病毒简介 勒索病毒是一种恶意软件&#xff0c;也称为勒索软件&#xff08;Ransomware&#xff09;&#xff0c;其主要目的是在感染计算机后加密用户文件&#xff0c;并要求用户支付赎金以获取解密密钥。这种类型的恶意软件通常通过电子邮件附件、恶意链接、下载的软件或漏洞…

JavaWeb JavaScript ① JS简介

目录 一、HTML&CSS&JavaScript的作用 二、前后端关联标签——表单标签 1.form标签 2.input标签 3.get/post提交的差异 4.表单项标签 5.布局相关标签 块元素——div 行内元素——span 三、CSS 1.CSS引入方式 方式1 行内式 方式2 内嵌式 方式3 外部样式表 2.CSS选择器 元…

Zabbix监控介绍与部署

目 录 一、zabbix介绍和架构 1.1 zabbix介绍 1.2 为什么需要监控 1.3 需要监控什么 二、zabbix使用场景与系统概述 2.1 zabbix的功能 2.2 zabbix架构 2.3 Zabbix术语 三、编译安装zabbix 3.1 安装依赖环境 3.2 建立管理用户 3.3 准备源码包&#xff0c;解压包 3.…

封装网络请求 鸿蒙APP HarmonyOS ArkTS

一、效果展示 通过在页面直接调用 userLogin(params) 方法&#xff0c;获取登录令牌 二、申请网络权限 访问网络时候首先需要申请网络权限&#xff0c;需要修改 src/main 目录下的 module.json5 文件&#xff0c;加入 requestPermissions 属性&#xff0c;详见官方文档 【声明权…