从零开始搞定类与对象(中)

运算符重载

  • 1.当运算符被用于类类型的对象时,C++语言允许我们通过运算符重载的形式指定新的含义。C++规定类类型对象使用运算符时,必须转换成调用对应运算符重载,若没有对应的运算符重载,则会编译报错。
  • 2. 运算符重载是具有特殊名字的函数,他的名字是由operator和后面要定义的运算符共同构成。和其他函数一样,它也具有其返回类型和参数列表以及函数体
  • 3. 重载运算符函数的参数个数和该运算符作用的运算对象数量一样多。一元运算符有一个参数,二元运算符有两个参数,二元运算符的左侧运算对象传给第一个参数,右侧运算对象传给第二个参数。
  • 4.如果一个重载运算符函数是成员函数,则它的第一个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少一个。
  • 5.运算符重载以后,其优先级和结合性与对应的内置类型运算符保持一致
  • 6.不能通过连接语法中没有的符号来创建新的操作符:比如operator@。7.
  • 7. .*     ::  sizeof   ?:    . 注意以上5个运算符不能重载。(选择题里面常考,大家要记一下)
  • 8.重载操作符至少有一个类类型参数,不能通过运算符重载改变内置类型对象的含义,如:int operator+(int x, int y)
  • 9. 一个类需要重载哪些运算符,是看哪些运算符重载后有意义,比如Date类重载operator-就有意义,但是重载operator*就没有意义。
  • 10.重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,无法很好的区分。C++规定,后置++重载时,增加一个int形参,跟前置++构成函数重载,方便区分
  • 11.重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第一个形参位置,第一个形参位置是左侧运算对象,调用时就变成了对象<<cout,不符合使用习惯和可读性。重载为全局函数把ostream/istream放到第一个形参位置就可以了,第二个形参位置当类类型对象。

 如代码,自定义类型进行使用“==”操作符会产生报错:

由于自定义类型比较复杂,所以默认情况下自定义类型是无法进行运算的,但是呢,有的自定义类型进行相关运算却是有意义的,比如我们经常遇到的日期类,两个日期相减得到的就是两个日期之间相隔的天数,日期加上天数就能得到另一个日期,学习运算符重载,我们可以让自定义类型进行运算,这样会更加方便。

话不多说,我们直接来看代码吧:

先写好日期类的代码,这是我们这一小节经常需要使用的类:

//日期类
#include<iostream>
using namespace std;
class Date
{
public:
//默认构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << '/' << _month << '/' << _day << endl;}
//在写运算符重载的算法中,我们不可避免的会用到类中的成员变量,所以需要将成员变量改为公有的,但其实我们一般不推荐这么做,在以后我们学习了友元函数就有更好地解决办法
//private:int _year;int _month;int _day;
};

现在,我们要对“==”运算符进行重载,也就是比较两个日期是否相同,按照运算符重载的特点:

//重载运算符名字:由operator和后面要定义的运算符共同构成
//具有返回值,参数,函数体
bool operator==(Date x1 ,Date x2)
{//将自定义类型的比较转化为成员变量中内置类型的比较return ((x1._year == x2._year) && (x1._month == x2._month) && (x1._day == x2._day));
}

写完这个代码后,再看是否还会有语法错误:

可以看到,当再次使用这个运算符时,就不会产生报错了。

另外,我们可以想一下上面那个运算符重载的代码还有啥可以改进的地方。我们在前面一节介绍过了,自定义类型的传值传参会调用拷贝构造,但是传引用传参就不会调用拷贝构造,所以在C++中,为了提高程序的性能,我们要习惯去使用引用传参:

//引用传参不需要调用拷贝构造
bool operator==(const Date& x1, const Date& x2)
{return ((x1._year == x2._year) && (x1._month == x2._month) && (x1._day == x2._day));
}

 那我们应当如何使用重载后的运算符呢?

方法一:函数调用的形式

bool ret= operator==( d1 , d2 );

方法二:就像内置类型一样直接使用运算符,一般情况下我们推荐这种写法

d1==d2;

现在我们就来运用一下这个运算符:

#include<iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << '/' << _month << '/' << _day << endl;}int _year;int _month;int _day;
};//引用传参不需要调用拷贝构造
bool operator==(const Date& x1, const Date& x2)
{return ((x1._year == x2._year) && (x1._month == x2._month) && (x1._day == x2._day));
}
int main()
{Date d1(2025, 8, 3);Date d2(2025, 8, 8);if (d1 == d2){cout << "两个日期相同" << endl;}else{cout << "两个日期不同" << endl;}return 0;
}

另外,为了在运算符重载中能够使用类里面的成员变量,我们将成员变量改为了公有的,但其实这种方式不太好,一种解决方法就是将运算符重载函数写到类里面去,我们来试一下:

#include<iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}bool operator==(const Date& x1, const Date& x2){return ((x1._year == x2._year) && (x1._month == x2._month) && (x1._day == x2._day));}void Print(){cout << _year << '/' << _month << '/' << _day << endl;}
private:int _year;int _month;int _day;};//引用传参不需要调用拷贝构造int main()
{Date d1(2025, 8, 3);Date d2(2025, 8, 8);return 0;
}

但是编译器会报错:

为啥就只是把运算符重载函数改到类里面就报错了呢?

这就是this指针在装神弄鬼了。表面上我们把函数写到类里面去是传递了两个参数,其实第一个参数的位置还有一个隐含的this指针。所以其实你是传递了3个参数的,但“==”只能接受2个操作数,所以会报错。

还需注意的是,在 C++ 中,当运算符重载函数作为成员函数定义在类内部时,this 指针指向的是运算符左侧的操作数对象的地址。这是运算符重载的核心规则之一。(这也就是第4点的意思)

所以,我们在类里面规定运算符重载时,我们自己写的参数个数应该比运算符实际能接受的操作数个数少一,比如,上面的代码应该改成:

#include<iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
//编译器会把它处理为:operator(Date*this,const Date& x2)bool operator==(const Date& x2){return ((_year == x2._year) && (_month == _month) && (_day == x2._day));}void Print(){cout << _year << '/' << _month << '/' << _day << endl;}
private:int _year;int _month;int _day;};//引用传参不需要调用拷贝构造int main()
{Date d1(2025, 8, 3);Date d2(2025, 8, 8);d1 == d2;//等价于:d1.operator==(d2);if (d1 == d2){cout << "两个日期相同" << endl;}else{cout << "两个日期不同" << endl;}return 0;
}

 .*     ::  sizeof   ?:    . 5个运算符不能重载,我们现在来简单讲一下.*运算符的用法(这个运算符的挺少用的,简单看一下就好),见以下代码和注释:

//成员函数指针的创建与访问:
#include<iostream>
using namespace std;
void func1()
{cout << "void func1()" << endl;
}
class A
{
public:void func2(){cout << "void func2()" << endl;}
};int main()
{//普通函数指针的创建:void (*pf1)() = func1;//利用函数指针来调用函数:(*pf1)();//类的成员函数指针的创建:void(A:: * pf2)() =& A::func2;//为啥类的成员函数指针要这样写://在 C++ 中,类成员函数指针的声明和赋值需要特殊语法,这是由成员函数的本质特性决定的//成员函数(非静态)与普通函数不同,它隐含一个 this 指针参数(用于访问对象实例的数据)// 因此,成员函数指针的语法需要体现:所属类(A::),调用时的对象绑定(通过 .* 或 ->* 运算符)//成员函数指针的调用
//想一下,利用成员函数的指针调用成员函数可以这么调用嘛:(*pf2)();//不可以的:因为成员函数中是有隐含的this指针的,this指针接收的是调用函数的对象的地址,所以在调用成员函数//时,还需要指定对象A aa;//利用成员函数指针调用函数:(aa.*pf2)();//这就是.*运算符的用途return 0;
}

再来讲解一下特点8是啥意思:

重载操作符至少有一个类类型参数:意思是当你重载一个运算符(如 +==<< 等)时,至少有一个参数必须是自定义的类(class)或结构体(struct)类型,而不能全部是基本类型(如 intdoublechar 等)。

这是因为:

  • C++ 不允许你修改基本类型(如 intfloat 等)的运算符行为,否则会导致代码混乱。

  • 运算符重载的目的是为了让自定义类型(如 DateStringVector 等)也能像内置类型一样使用运算符。

 不能通过运算符重载改变内置类型对象的含义:意思是 你不能改变基本类型(如 intfloatchar 等)的运算符的原始行为。

这是因为:

  • 如果允许修改基本类型的运算符行为,比如让 1 + 1 返回 3,那代码会变得极其混乱,无法维护。

  • C++ 只允许你为自定义类型(如 DateString)定义新的运算符行为,而不能篡改内置类型的运算规则。

 赋值运算符重载

赋值运算符重载是一个默认成员函数,用于完成两个已经存在的对象直接的拷贝赋值,这里要注意跟拷贝构造区分,拷贝构造用于一个对象拷贝初始化给另一个要创建的对象。

赋值运算符重载的特点

1. 赋值运算符重载是一个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成const 当前类类型引用,否则会传值传参会有拷贝
2. 有返回值,且建议写成当前类类型引用,引用返回可以提高效率,有返回值目的是为了支持连续赋值场景。
3. 没有显式实现时,编译器会自动生成一个默认赋值运算符重载,默认赋值运算符重载行为跟默认拷贝构造函数类似,对内置类型成员变量会完成值拷贝/浅拷贝(一个字节一个字节的拷贝),对自定义类型成员变量会调用他的赋值重载函数。
4. 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的赋值运算符重载就可以完成需要的拷贝,所以不需要我们显示实现赋值运算符重载。像Stack这样的类,虽然也都是内置类型,但是_a指向了资源,编译器自动生成的赋值运算符重载完成的值拷贝/浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝(对指向的资源也进行拷贝)。像MyQueue这样的类型内部主要是自定义类型Stack成员,编译器自动生成的赋值运算符重载会调用Stack的赋值运算符重载,也不需要我们显示实现MyQueue的赋值运算符重载。这里还有一个小技巧,如果一个类显示实现了析构并释放资源,那么他就需要显示写赋值运算符重载,否则就不需要。 

写代码来理解这些特点:

#include<iostream>
using namespace std;
class Date
{
public://构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//拷贝构造:Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}//赋值运算符重载:如果是传值传参,在调用赋值运算符重载时还会调用拷贝构造//但这里是传引用传参,不会调用拷贝构造void operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << _year << '/' << _month << '/' << _day << endl;}
private:int _year;int _month;int _day;};//引用传参不需要调用拷贝构造int main()
{Date d1(2025, 8, 3);
//拷贝构造Date d2(d1);//赋值构造Date d3(2025, 7, 8);d3 = d1;d1.Print();d2.Print();d3.Print();return 0;
}

在调试过程中,确实调用了赋值运算符重载: 

 还需要区分一个点,如果在main函数中有这样一行代码:

Date d4 = d1;

请问这个代码是会调用拷贝构造还是赋值运算符重载?

答案是赋值运算符重载,可以看到d4这个对象是在创建的时候同时让对象d1对其进行初始化,这就是拷贝构造的另一种写法,可能容易与拷贝构造混淆,这一点在上一节我们也是讲到过的哦。

看到这里,就有一个注意的地方,我们上面写的赋值运算符重载是有一点小小的错误的哦,特点2已经告诉我们了,赋值运算符重载是有返回类型的哦,这样才能支持连续赋值。

那么我们可以想一下:如果有两个日期类对象d1和d3,我们要执行d1=d3,那么赋值运算符重载的返回值是什么呢?返回的应当是d1中的内容,因为是要把d3赋值给d1

	//执行:d1=d3//等价于:  d1.operator(d3)//形参部分:operator=(const Date& d)//实际上,编译器会将代码转化为:operator=(Date* this , const Date& d)//实参的传参部分:d1.operator(&d1 , d3 )//函数体内部://{//   this->	_year = d._year;//   this->	_month = d._month;//	  this->_day = d._day;//}//最后返回值返回的应该是d1这个对象,而在参数中,d1这个对象的地址实际上传给了this,所以可以通过*this获得d1
//最终代码:Date& operator=(const Date& d){_year =d._year;_month = d._month;_day = d._day;return *this;}
//为什么这里可以用引用返回
//原因一:因为*this并不是局部对象,*this得到的就是d1,他是在main函数中定义的,出了赋值运算符重载的作用域以后*this对应的空间并没有被销毁,所以这里可以传引用返回
//原因二:如果这里是传值返回,就要再调用拷贝构造函数,这样比较麻烦

看到这里,其实代码还可以再修改一下,想一下,假设有一种情况,有的小伙伴执行:d1=d1这种自己给自己赋值的代码(虽然这种代码无意义,但难免会有人真这么做),我们就可以把代码改成这样:

Date& operator=(const Date& d)
{
//当自己给自己复制时,this表示的是d1的地址,d是d1的别名,&d也就是&d1
//即this==&d1if(this!=&d){_year =d._year;_month = d._month;_day = d._day;}return *this;
}

日期类的实现

上面讲了这么多,我们就一起来实践一下,来完成日期类的实现吧!!!

//Date.h
#pragma once
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;class Date
{
public://构造函数的声明Date(int year = 1900, int month = 1, int day = 1);//打印函数的声明void Print();
//日期的相关比较函数bool operator<(const Date& d);bool operator<=(const Date& d);bool operator>(const Date& d);bool operator>=(const Date& d);bool operator==(const Date& d);bool operator!=(const Date& d);// d1 += 天数Date& operator+=(int day);Date operator+(int day);// d1 -= 天数Date& operator-=(int day);Date operator-(int day);// d1 - d2int operator-(const Date& d);// ++d1 -> d1.operator++()Date& operator++();// d1++ -> d1.operator++(0)// 为了区分,构成重载,给后置++,强行增加了一个int形参// 这里不需要写形参名,因为接收值是多少不重要,也不需要用// 这个参数仅仅是为了跟前置++构成重载区分Date operator++(int);Date& operator--();Date operator--(int);//得到一月有多少天:int GetDayOfMonth(int year,int month){int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if (month == 2 && ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0))){arr[month]++;}return arr[month];}private:int _year;int _month;int _day;
};//Date.cpp
#define  _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
//注意:全缺省类默认构造函数的缺省参数只在声明中写,不在定义中写
Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}void Date::Print()
{cout << _year << '/' << _month << '/' << _day << endl;
}bool Date::operator==(const Date& d)
{return ((_year == d._year) && (_month == d._month) && (_day == d._day));
}bool Date::operator!=(const Date& d)
{return !(*this == d);
}
bool Date::operator>=(const Date& d)
{return !(*this < d);
}
bool Date::operator<=(const Date& d)
{return !(*this > d);
}
bool Date::operator< (const Date & d)
{return (_year < d._year) ||((_year == d._year) && (_month < d._month)) ||((_year == d._year) && (_month == d._month) && (_day < d._day));
}
bool Date::operator> (const Date& d)
{return (*this != d) && (!(*this < d));
}Date& Date::operator+=(int day)
{_day += day;while (_day > GetDayOfMonth(_year, _month)){_day -= GetDayOfMonth(_year, _month);_month++;if (_month == 13){_year++;_month = 1;}}return *this;
}
//不改变*this
Date Date::operator+(int day)
{Date d1 = *this;d1 += day;return d1;
}
//
//// d1 -= 天数
Date& Date:: operator-=(int day)
{if (_day > day){_day -= day;return *this;}while (_day <= day){_month--;if (_month == 0){_year--;_month = 12;}_day += GetDayOfMonth(_year, _month);if (_day > day){_day -= day;break;}}return *this;
}
Date Date::operator-(int day)
{//Date d(*this);Date d = *this;d -= day;return d;
}
////后置加加:有拷贝构造
Date Date::operator++(int)
{//Date d(*this);Date d = *this;(*this) += 1;return d;
}
//前置--
Date& Date:: operator--()
{*this -= 1;return *this;
}
Date Date::operator--(int)
{//Date d(*this);Date d = *this;(*this) -= 1;return d;
}
//前置++:没有拷贝构造
Date& Date::operator++()
{*this += 1;return *this;
}
////
//// d1 - d2
int Date::operator-(const Date& d)
{//暴力搜索Date d1 = d;assert(*this > d);int count = 0;while (*this != d1){d1++;count++;}return count;
}// d1 += 100
//Date& Date::operator+=(int day)
//{
//	*this = *this + day;
//	return *this;
//}
//
//// d1 + 100
//Date Date::operator+(int day)
//{
//	Date tmp(*this);
//
//	tmp._day += day;
//	while (tmp._day > GetMonthDay(tmp._year, tmp._month))
//	{
//		tmp._day -= GetMonthDay(tmp._year, tmp._month);
//		++tmp._month;
//		if (tmp._month == 13)
//		{
//			++tmp._year;
//			tmp._month = 1;
//		}
//	}
//这是+和+=的另外一套写法,上面一种写法是让+去复用+=的逻辑,会经过2次复制拷贝
//这一种写法是让+=去复用+,会经过3次拷贝
//所以我们选用上一种方法:让+去复用+=//test.cpp
#include"Date.h"
#include<iostream>
using namespace std;
int main()
{Date d1(2025, 1, 1);//Date d2(d1);//Date d3 = d2 + 100;//d3.Print();//d1 -= 100;//d1.Print();//Date d2 = d1 - 100;//d2.Print();/*++d1;*///d1.Print();//Date d2=d1++;//d1.Print();//d2.Print();//Date d2=d1--;//d1.Print();//d2.Print();//Date d2=--d1;//d1.Print();//d2.Print();Date d2(2024, 9, 27);Date d3 = d2 + 96;d3.Print();int gap = d1 - d2;cout << gap << endl;return 0;
}

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

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

相关文章

SpringMVC实战指南:从环境搭建到功能实现全解析

第一章&#xff1a;SpringMVC环境搭建与基础配置1.1 Maven依赖配置在Maven项目中&#xff0c;SpringMVC的依赖配置是开发的第一步。根据Spring官方推荐&#xff0c;以下是SpringMVC 5.3.x版本的Maven依赖配置&#xff1a;<dependencies><!-- Spring MVC核心依赖 -->…

Repo 与 manifest

Manifest&#xff1a;它本身就是一个 git 仓库&#xff0c;其中存放的都是包含仓库和子仓库信息的XML文件。这些文件全部由开发者或者维护者手动配置并自己上传到 git 仓库。另外&#xff1a;Manifest 中的仓库之间的依赖关系 repo 也并不关心。所以它们可以是同级的也可以是包…

深入浅出 RabbitMQ:简单队列实战指南

大家好&#xff0c;我是工藤学编程 &#x1f989;一个正在努力学习的小博主&#xff0c;期待你的关注实战代码系列最新文章&#x1f609;C实现图书管理系统&#xff08;Qt C GUI界面版&#xff09;SpringBoot实战系列&#x1f437;【SpringBoot实战系列】SpringBoot3.X 整合 Mi…

Ubuntu22-Qt Creator-fcitx-中文输入

fcitx在ubuntu系统中路径 /usr/lib/x86_64-linux-gnu/qt5/plugins/platforminputcontexts/ /usr/lib/x86_64-linux-gnu/qt6/plugins/platforminputcontexts/ fcitx-qt5-1.2.7 编译 下载链接:https://github.com/fcitx/fcitx-qt5/archive/refs/tags/1.2.7.zip Qt版本:Qt C…

【Java基础|第十三篇】面向对象基础(三)——继承(一)继承的理解,实现,特点……

&#xff08;四&#xff09;面向对象&#xff1a; 5、继承&#xff1a; &#xff08;1&#xff09;理解&#xff1a; 概念&#xff1a; 继承是面向对象的三大特征之一 继承是类与类之间关系的一种&#xff08;是父类与子类的关系&#xff09; 使用场景&#xff1a; 一个类与另…

QGIS绿色版吉林一号切片体验版插件(Jilin1Tiles)更新

吉林一号更新2024年图源了但吉林一号切片体验版插件&#xff08;Jilin1Tiles&#xff09;还没有更新&#xff0c;我修改了一下代码&#xff0c;直接集成到QGIS绿色版中。如下&#xff1a;注意&#xff1a;第一次使用的时候需要选中启用一下插件&#xff1a;需要使用的可以直接下…

git操作命令和golang编译脚本

git子模块信息处理命令git init submodule git submodule updategit取消合并 git merge --abort git reset --hard HEAD{1}bat文件生成二进制set GOOSlinux set GOARCHamd64 go env -w GOFLAGS-modvendor go build -ldflags "-w -s" -ohallapiset GOOSlinux set GOAR…

通往L4之路:构建自我进化的智能驾驶决策大脑

摘要&#xff1a; 本文旨在提出一个超越当前主流“感知-预测-规划”分离式架构的下一代自动驾驶决策系统方案。面对自动驾驶领域最核心的“长尾场景”难题&#xff0c;本文借鉴并升华了一套源于复杂策略制定的决策智能框架&#xff0c;通过构建动态驾驶世界模型&#xff08;Dyn…

AI编程助手:终结996的新希望

引言程序员工作现状与“996”现象的普遍性AI技术快速发展对编程效率的潜在影响核心问题&#xff1a;AI IDE与AI辅助编程能否改变传统开发模式AI IDE与AI辅助编程的核心技术AI IDE的定义与功能&#xff08;代码补全、错误检测、自动重构等&#xff09;AI辅助编程工具&#xff08…

Anthropic 禁止 OpenAI 访问 Claude API:商业竞争与行业规范的冲突

Anthropic 禁止 OpenAI 访问 Claude API&#xff1a;商业竞争与行业规范的冲突 文章来源&#xff1a;Poixe AI 本周&#xff0c;美国 AI 公司 Anthropic 宣布禁止 OpenAI 通过 API 访问其 Claude 系列大模型。这一举动引发了行业对"友好基准测试"与商业竞争边界的热…

区块链 + 物联网落地案例:供应链溯源系统开发全记录

本文详细记录了区块链与物联网技术融合的供应链溯源系统开发全流程。从项目背景出发&#xff0c;阐述传统供应链溯源痛点&#xff0c;介绍系统开发的技术架构设计&#xff0c;包括物联网数据采集层、区块链数据存储层等核心模块&#xff0c;详解硬件选型、智能合约编写、数据上…

Windows环境下Intel Fortran如何安装配置NetCDF

NetCDF(Network Common Data Form)格式,简称nc格式,是一种自描述、与平台无关的二进制数据文件,特别适合多维数据的存储和交换,广泛应用于气象、海洋、地球科学等领域。本文介绍Windows环境下IntelFortran安装配置NetCDF的过程。 一、系统环境及准备工作 1. 系统 Wind…

tcp/udp的socket特点

tcp &#xff1a; 绑定一个 socket 只是用来监听&#xff0c;accept 对每个客户端生成一个 socket 用来维护滑动窗口等。每个客户端用一个 socket 用来维护滑动窗口等。 4 次挥手对应两次 close 的 fin 和返回的 ack。 而三次挥手在 connect 里阻塞完成。 ​udp &#xff1a; 双…

Linux命令top

top一、 命令二、 如何查看top输出的结果一、 命令 top命令是Linux中的一个实时进程监控工具&#xff0c;类似于windows中的任务管理器。 基本命令 top二、 如何查看top输出的结果 我们需要分析top输出的结果 top输出的结果分为上下两部分&#xff0c;先看上半部分 第一行是…

Perl 数据库连接

Perl 数据库连接 概述 Perl是一种强大的编程语言&#xff0c;广泛应用于文本处理、系统管理、网络编程等领域。随着数据库技术的快速发展&#xff0c;Perl与数据库的结合也日益紧密。本文将详细介绍Perl数据库连接的相关知识&#xff0c;包括常用的数据库类型、连接方法以及一些…

jenkins从入门到精通-P1—九五小庞

1. jenkins的两个核心为CI持续集成 CD持续部署2.jenkins在企业工作中的流程3. 学习的内容包括

第九节 Redis 事务、Redis 脚本

Redis 事务可以一次执行多个命令&#xff0c; 并且带有以下两个重要的保证&#xff1a; 事务是一个单独的隔离操作&#xff1a;事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中&#xff0c;不会被其他客户端发送来的命令请求所打断。事务是一个原子操作&#x…

托福阅读37-2

托福阅读37-2 1.reinforcement from reintroduction定位到倒数第二句&#xff0c;这里我没看懂former和term&#xff0c;直接懵掉了&#xff0c;然后往后看。这句话其实省略了&#xff0c;补充完应该是The former is termed reintroduction and the latter is termed reinforce…

docker-compose一键部署Springboot+Vue前后端分离项目

1. 背景说明 后端使用JDK8&#xff0c;前端为普通Vue项目前端访问后端接口&#xff0c;统一带了前缀/api 2. 项目配置 2.1 后端 yml文件里配置统一访问前缀/api2.2 前端 API路径配置为相对路径&#xff1a;说明&#xff1a;我这边前后端应用都是部署在同一台服务器上&#xff0…

Unity_数据持久化_XML基础

Unity数据持久化 三、XML数据持久化 3.1 XML基础概念 3.1.1 什么是XML XML&#xff08;eXtensible Markup Language&#xff09;**是一种可扩展的标记语言&#xff0c;用于存储和传输数据。它具有以下特点&#xff1a; 结构化&#xff1a;数据以层次结构组织可读性&#xff1a;…