深入篇【C++】类与对象:运算符重载详解 -上

news2024/11/17 11:56:11

深入篇【C++】类与对象:运算符重载详解 -上

  • ⏰.运算符重载
    • 🕓Ⅰ.<运算符重载
    • 🕐Ⅱ.>运算符重载
    • 🕒Ⅲ.==运算符重载
    • 🕑Ⅳ.=运算符重载
      • ①.格式
        • 1.改进1
        • 2.改进2
      • ②.默认成员函数
        • 1.功能
        • 2.不足

在这里插入图片描述

⏰.运算符重载

内置类型(int /double…… )是可以之间进行运算符之间的比较的,因为编译器知道它们之间的比较规则,可以之间转化为指令。
那如果自定义类型能否之间进行运算符之间的比较呢?当然不能了,因为编译器是不知道这个自定义类型的规则是什么,不知道如何进行比较。
1.内置类型是可以之间比较的。
2.自定义类型是无法之间进行比较的。
那如何使自定义类型也能进行比较呢?这时C++给出了办法,让这个运算符重载成一个函数,当自定义类型进行比较时,其本质就是在调用重载函数。

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有器返回值类型,函数名字以及参数列表,其返回值类型与参数列表和普通函数类似。

函数的名字:关键字operator后面加需要重载的运算符符号。
函数的原型为:

返回值类型  operator运算符符号(参数列表)

【注意】

  • 不能通过连接其他符号来创建一个新的操作符。比如:operator#
  • 重载函数参数必须至少有一个为自定义类型参数,不能全是内置类型参数。
  • 用于内置类型的运算符,其含义是不能改变的。原本的运算符对内置类型的含义是不变的。
  • 重载函数作为成员函数时,其形参看起来要比操作数目少一个,那是因为成员函数的第一个参数默 认为this指针。
  • *. / sizeof / :: / ? : / .`这五个运算符是不能被重载的。

那所有的运算符都可以重载吗?
当然不是,哪些运算符能够重载呢?–>哪些对类有用的运算符就可以实现重载。

我们先写一个日期类的对象:

class Data
{
public:
	Data(int year = 2023, int month = 5, int day = 3)
	{
		_year= year;
		_month = month;
		_day = day;
	}
	
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
  Data d1(2023,2,1);
  Data d2(2023,5,6);
}

下面我们将对这个日期对象进行运算符之间的比较,比较两个日期的是否大于或者小于。

🕓Ⅰ.<运算符重载

class Data
{
public:
	Data(int year = 2023, int month = 5, int day = 3)
	{
		_year= year;
		_month = month;
		_day = day;
	}
	
private:
	int _year;
	int _month;
	int _day;
};
bool Less(const Data& x1, const Data& x2)
{
	if (x1._year < x2._year)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month < x2._month)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
	{
		return true;
	}
	return false;
}
int main()
{
  Data d1(2023,2,1);
  Data d2(2023,5,6);
  Less(d1,d2);
}
```c

我们写这个Less函数是用来比较第一个对象是否小于第二个对象的,这种写法跟我们以前写的函数是差不多的,不过有时候这种写法会出现问题,那就是比较专业的人会按照功能给函数命名,而不是很专业的人呢,就会乱起名字,导致我们可能不知道这个函数是干什么用,这里函数Less一看就知道是看是否小于了,如果写成别的可能就认不出来了,所以C++就给出了运算符重载形式,统一运算符,让运算符既可以对内置类型起作用,又可以对自定义类型起作用,这样又方便,又好识别。
C++规定运算符重载的写法如下:

返回值 operator>(参数)

所以上面的Less函数就可以写成这样的运算符重载函数:

bool operator<(const Data& x1,const Data& x2)
{
	if (x1._year < x2._year)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month < x2._month)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
	{
		return true;
	}
	return false;
}

这个函数就是对<运算符进行重载,使这个运算符<可以被自定义类型使用,但注意的是,<运算符对内置类型的运算规则没有改变,仍然遵守原先的。

也就是当自定义类型使用这个<运算符时,编译器会自动跳到这个运算符重载函数里。
自定义类型使用运算符比较本质上就是在调用函数。

int main()
{
  Data d1(2023,2,1);
  Data d2(2023,5,6);
  //< 运算符重载之后,自定义类型就可以使用这个运算符了。
  //两个自定义类型之间的比较就可以直接这样写:
  d1<d2;
  //而自定义类型使用运算符本质上就是去调用函数:本质上就是这样:
  operator(d1,d2);
  //这两种写法都是一样的。
}

在这里插入图片描述

只不过这里有个问题,那就是该重载函数写在类外,无法访问类里面的私有成员,得先将类里面的成员变成公有的才可以访问。那么问题来咯,封装性如何保证?

【解决方法】
将该运算符重载函数直接写到类里面,变成成员函数。那样就可以直接访问成员变量了。

class Data
{
public:
	Data(int year = 2023, int month = 5, int day = 3)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	bool operator<(const Data& x)//参数只有一个,还有一个传给隐藏的this了
	{
		if (_year < x._year)
		{
			return true;
		}
		else if (_year == x._year && _month < x._month)
		{
			return true;
		}
		else if (_year == x._year && _month == x._month && _day < x._day)
		{
			return true;
		}
		return false;
	}
private:
	int _year;
	int _month;
	int _day;
};

不过写成成员函数要注意一点就是,成员函数会默认将将调用的对象传给隐藏的this指针,而参数看起来少了一个,其实不是,只是将调用的对象传给隐藏的this了。所以在写成成员函数时,当操作数有两个时,那么参数就只有一个。

int main()
{
  Data d1(2023,2,1);
  Data d2(2023,5,6);
  d1<d2;
  //运算符重载函数写成成员函数后,那么对应的函数调用也就不一样了
  //原来  d1<d2 === operator<(d1,d2);
  //现在  d1<d2 === d1.operator(d2);
}

在这里插入图片描述

🕐Ⅱ.>运算符重载

有了上面的<运算符重载,这个>运算符重载和它是一样的。

class Data
{
public:
	Data(int year = 2023, int month = 5, int day = 3)
	{
		_year= year;
		_month = month;
		_day = day;
	}
	
private:
	int _year;
	int _month;
	int _day;
};
bool operator>(const Data& x1, const Data& x2)//运算符重载
{
	if (x1._year > x2._year)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month > x2._month)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month == x2._month && x1._day > x2._day)
	{
		return true;
	}
	return false;
}
int main()
{
  Data d1(2023,2,1);
  Data d2(2023,5,6);
  d1>d2;
  //等同于 
  operator>(d1,d2);
}

写到类外时就无法访问类里的私有成员,这是这里的问题,所以还是写到类里面来,弄成成员函数。
当写成成员函数时,就要注意参数只有一个。

class Data
{
public:
	Data(int year = 2023, int month = 5, int day = 3)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
	bool operator>( const Data& x)//>运算符重载
	{
		if (_year > x._year)
		{
			return true;
		}
		else if (_year == x._year && _month > x._month)
		{
			return true;
		}
		else if (_year == x._year && _month == x._month &&_day > x._day)
		{
			return true;
		}
		return false;
	}
private:
	int _year;
	int _month;
	int _day;
};

并且要理解自定义类型使用>运算符时是在调用成员函数operator>(d2);

int main()
{
  Data d1(2023,2,1);
  Data d2(2023,5,6);
  d1>d2;
  //等同于 
  d1.operator>(d2);
}

🕒Ⅲ.==运算符重载

//全局的operator==重载函数
class Data
{
public:
	Data(int year = 2023, int month = 5, int day = 3)
	{
		_year= year;
		_month = month;
		_day = day;
	}
	
private:
	int _year;
	int _month;
	int _day;
};
bool operator==(const Data& d1, const Data& d2)
{
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}
int main()
{
  Data d1(2023,2,1);
  Data d2(2023,5,6);
  d1==d2;
  //本质和下面一样
  operator==(d1,d2);
}

当写成成员函数时:

class Data
{
public:
	Data(int year = 2023, int month = 5, int day = 3)
	{
		_year= year;
		_month = month;
		_day = day;
	}
     // bool operator==(Date* this, const Date& d2)
    // 这里需要注意的是,左操作数是this,指向调用函数的对象
    bool operator==(const Date& d2)
 {
        return _year == d2._year;
            && _month == d2._month
            && _day == d2._day;
 }
private:
 int _year;
 int _month;
 int _day;
};

要理解自定义类型使用>运算符时是在调用成员函数 operator= =(d2);

int main()
{
  Data d1(2023,2,1);
  Data d2(2023,5,6);
  d1==d2;
  //等同于 
  d1.operator==(d2);
}

🕑Ⅳ.=运算符重载

赋值运算符重载就是对=运算符进行重载,使自定义类型也可以使用=运算符。
赋值就相当于将自己拷贝给对方,而它与拷贝构造函数的区别是什么呢?

1.运算符重载:是已存在的两个对象之间复制拷贝
2.拷贝构造函数:用一个已经存在的对象去初始化一个新对象

注意:下面的是拷贝构造而不是赋值运算符重载。

Data d1(2023,5,1);
Data d2=d1;
虽然用了=运算符,但是记住赋值运算符重载的条件是两个已经存在的对象之间进行赋值拷贝,而d2是新创建的对象。

①.格式

  • 参数类型:引用传参,用const修饰,即const 类&
    引用传参可以提高传参效率。
  • 返回值类型:引用返回,即 类&
    引用返回可以提高返回的效率,有返回值目的是为了支持连续赋值功能。
  • 要检查是否给自己赋值
  • 返回*this:要符合连续赋值的含义。

重载函数形式:返回值 operator=(参数)

class Data
{
public:
	Data(int year = 2023, int month = 5, int day = 3)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void operator=(const Data& d)//赋值运算符重载
	{
			_year = d._year;
			_month = d._month;
			_day = d._day;
	}
	private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Data d1(2023, 5, 1);
	Data d2(2023, 5, 6);
	d1 = d2;

}

在这里插入图片描述
我们可以发现将赋值运算符重载后,自定义类型也可以使用赋值运算符了,而且使用的很成功,将d2的值赋给了d1。
并且我们要理解自定义类型使用运算符本质是在调用函数,也就是可以写成这样:

int main()
{
	Data d1(2023, 5, 1);
	Data d2(2023, 5, 6);
	d1 = d2;
	//本质是调用函数,这两者是一样的
	d1.operator=(d2);
}

在这里插入图片描述
那这样就没有问题了吗?
我们知道在对内置类型进行赋值时我们可以连续赋值比如这样:

int a,b,c;
a=b=c=0;

赋值的顺序从右到左,0先赋值给c,然后得到一个结果,再赋值给b,得到一个结果,再赋值给a。

那自定义类型能否完成这样的连续赋值操作呢?
即这样:


	Data d1(2023, 5, 1);
	Data d2(2023, 5, 6);
	Data d3(2023, 9, 9);
	d1=d2=d3;

在这里插入图片描述
我们发现这样的操作编译器是无法编译通过的,为什么呢?
这是因为当调用运算符重载函数时,返回值是void,而调用完的结果要再赋值给d1,而void和d1的类型无法匹配。
在这里插入图片描述
而要支持上面的连续赋值,需要将赋值重载函数的返回值写成调用该函数的类型,这样当对象调用完后,返回的仍然是同类型,这样就可以支持连续赋值了。

class Data
{
public:
	Data(int year = 2023, int month = 5, int day = 3)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Data operator=(const Data& d)
	{
			_year = d._year;
			_month = d._month;
			_day = d._day;
			return *this;
	}
	private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Data d1(2023, 5, 1);
	Data d2(2023, 5, 6);
	Data d3(2023, 9, 9);
	d1 = d2 = d3;

在这里插入图片描述

1.改进1

这样我们的自定义类型就可以很好的使用赋值运算符啦,接下来我们来对这个赋值运算符重载函数进行一些改进,哪些地方可以改进呢?
我们的参数是引用传参,这样可以减少拷贝,提高效率,那该函数的返回值是否可以用引用返回呢?
思考一下,我们返回的是什么?我们返回值的是调用对象,即this指针指向的对象。
而当该函数结束时,this指针会销毁,但this指针指向的对象还在呀,该对象并不在运算符重载函数里面
(*this是在该函数里面,作为形参压入函数栈帧)。
所以我们可以用引用返回,这样就可以减少拷贝,提高效率啦。

Data& operator=(const Data& d)//用引用返回,可以提高效率
	{
			_year = d._year;
			_month = d._month;
			_day = d._day;
			return *this;
	}

2.改进2

我们要求不要对自己赋值,所以在赋值之前我们可以进行检查,看是否对自己赋值,只要检查两个对象的地址是否相同即可:

Data operator=(const Data& d)
	{
		if (this != &d)//如果两个对象的地址不相同那么就可以进行赋值
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
			return *this;
		}
	}

②.默认成员函数

赋值运算符也是作为默认成员函数的。

而默认成员函数有一个规定:只能写在类里面,不能写在类外面
(但是可以在类里面声明,在类外定义,声明和定义分开)

为什么呢?

因为默认成员在用户不显示写时,它会自动生成一个默认函数,帮你完成赋值工作。

而当你在类外写赋值重载函数时,编译器会认定你没有写,它会自动生成一个默认函数,那这样就和编译器生成的运算符重载冲突了,所以赋值运算符只能写在类里面。
在这里插入图片描述
总结:
赋值运算符不显示写,编译器会生成一个默认的,如果用户再在类外自己实现一个全局的赋值运算符重载,就和编译器生成的默认函数冲突了,故赋值运算符重载只能是类的成员函数。

1.功能

默认成员函数的意义就是,用户不显示实现,编译器会自动生成一个默认的函数,来帮你实现。
那编译器生成的默认函数的具体是如何实现的呢?它的具体功能是什么呢?

当用户不显示实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝,即浅拷贝。

即默认生成赋值运算符重载与默认生成的拷贝构造函数行为一致:
1.内置类型成员是之间赋值–值拷贝/浅拷贝。
2.自定义类型成员会去调用对应的它的赋值运算符重载,如果没有那也是浅拷贝。

class Time//时间类
{
public:
	Time()
	{
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	Time& operator=(const Time& t)
	{
		if (this != &t)
		{
			_hour = t._hour;
			_minute = t._minute;
			_second = t._second;
		}
		return *this;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date//日期类
{
private:
	// 基本类型(内置类型)
	int _year = 2023;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d1;
	Date d2;
	d1 = d2;
	return 0;
}

我们先来分析一下该代码,该代码有两个类,一个是时间类Time,一个是日期类Data。
日期类里面的成员变量有内置类型又有自定义类型,并且没有写任何函数。
但我们知道编译器会给这个类自动生成一些默认函数,其中就有赋值运算符重载,接下来我们就看看,编译器生成的赋值运算符重载怎么样。

在这里插入图片描述

所以显而易见,对于这样日期类,时间类的,编译器自动生成的赋值运算符是可以满足使用的。
即编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝,这种已经符合部分类的需求了,那还要我们自己实现吗?

2.不足

当类成员涉及有关资源的开辟时,编译器生成的默认成员函数就无法使用。

typedef int DataType;
struct stack//class可以定义一个类
{
public://访问限定符
	stack(int capacity = 4)//缺省值
	{
		cout << "stack(int capacipty=4)" << endl;
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		_capacity = capacity;
		_size = 0;
	}
	void Push(DataType data)
	{
		_array[_size] = data;
		_size++;
	}
	void Pop()
	{
		if (Empty())
		{
			return;
		}
		--_size;

	}
	int Empty()
	{
		return _size == 0;
	}

	~stack()
	{
		cout << "~stack()" << endl;
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private://访问限定符
	DataType* _array;
	int _capacity;
	int _size;
};
int main()
{
	stack s1;
	stack s2;
    s2=s1;//使用编译器自动生成的赋值运算符重载函数来赋值
 }

这样会出现什么问题呢?上一篇拷贝构造函数其实就讲了,因为它和拷贝构造函数的功能类似,所以遇到的问题也是类似。
因为编译器生成的默认函数都是浅拷贝,对于一些开辟了空间资源的变量,浅拷贝是完完全全的拷贝,没有动过任何处理,所以这就涉及赋值后,两个对象里面的变量都指向同一块空间,都属于同一块区域。那么这问题就大了去了。因为会出现析构两次,和我改变你也改变,你改变我也改变的状态。

1.同一块空间会析构两次,会报错。
当s2对象生命周期结束时,系统自动调用析构函数来清理数据,那么*a指向的空间就被销毁了。
而当s1对象生命周期结束时,又析构一次相同的空间,这样同一块空间就析构两次了。
2.一个变量修改会影响另一个变量,因为两个变量都存在同一块空间里。

所以编译器生成的赋值重载函数是有不足地方的,不能全部都依赖编译器生成的函数。不过对于日期类的,MyQueue类的编译器都可以直接实现功能,对于Stack类的就不可以了。

1.所以如果类中没有涉及资源管理,赋值运算符是否实现都是可以的;
2.但是一旦涉及资源管理就必须自己实现。
在这里插入图片描述

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

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

相关文章

二分法相关使用

文章目录 1. 在一个有序数组中,找某个数是否存在2. 在一个有序数组中,找大于等于某个数最左侧的位置3. 在一个有序数组中, 找小于等于某个数最右侧的位置4. 局部最大值问题 1. 在一个有序数组中,找某个数是否存在 在线OJ&#xff1a;704. 二分查找 有序数组下的二分思路如下:…

亚马逊:分布式计算宣言

文章目录 分布式计算宣言背景关键概念基于服务的模型基于工作流的模型和数据域应用概念跟踪状态变化对进行中的工作流程元素进行更改工作流程和 DC 客户订单处理 分布式计算宣言 创建时间&#xff1a; 1998 年 5 月 24 日 修订日期&#xff1a; 1998 年 7 月 10 日 背景 很…

uni-app nvue页面中使用video视频播放组件

我遇到的问题是&#xff0c;在nvue页面引用video组件&#xff0c;然后啥也没显示的&#xff0c;显示了无法控制播放&#xff0c;折腾了好久&#xff0c;在这里记录下来&#xff01;希望可以帮助到需要的人 我的代码是这样的&#xff08;src换成官方的举例&#xff09; <vi…

查看mysql数据库版本的方式(cmd命令和navicat)

目录 一、使用场景 二、查看方式 &#xff08;一&#xff09;cmd命令方式 &#xff08;二&#xff09;navicat16软件里面查看 &#xff08;三&#xff09;navicat试用版本查看的方式 一、使用场景 在有些时候需要调试系统的时候&#xff0c;就要看数据库的版本&#xff…

【致敬未来的攻城狮计划】— 连续打卡第二十三天:RA2E1的存储器基础知识

系列文章目录 1.连续打卡第一天&#xff1a;提前对CPK_RA2E1是瑞萨RA系列开发板的初体验&#xff0c;了解一下 2.开发环境的选择和调试&#xff08;从零开始&#xff0c;加油&#xff09; 3.欲速则不达&#xff0c;今天是对RA2E1 基础知识的补充学习。 4.e2 studio 使用教程 5.…

【MySQL】数据库基础操作一:建库与建表

目录 &#x1f31f;前言 &#x1f308;1、常见的关系型数据库 &#x1f31f;数据库的基本操作 &#x1f308;1、常用数据库的操作 &#x1f308;2、常用的数据类型 &#x1f308;3、表的基本操作 &#x1f345;创建表的一个小练习 &#x1f31f;前言 &#x1f…

基于Python的特征工程:数据预处理(一)

一、概述 特征工程是机器学习工作流程中不可或缺的一环&#xff0c;它将原始数据转化为模型可理解的形式。数据和特征的质量决定了机器学习的上限&#xff0c;而模型和算法则是逼近这个上限的手段。因此&#xff0c;特征工程的重要性不言而喻。其主要工作涉及特征的采集、预处…

干货 | 带你用Process完成中介效应检验

Hello&#xff0c;大家好&#xff01; 这里是壹脑云科研圈&#xff0c;我是喵君姐姐~ 今天我们给大家介绍下平常常用的也很简单的操作&#xff0c;如何使用spss中process插件进行中介效应检验。 Process的功能很强大&#xff0c;它的下载也是免费开源的&#xff0c;在spss的…

springboot项目:瑞吉外卖 前后端 代码、思路 详细分析 part5

part1 part2 part3 part4 part5 本页 6 移动端短信发送和手机验证码登入 6.1 短信发送 6.2 手机验证码登入 6.1 短信发送 6.1.1整体分析 2. 3. 注册登入阿里云账户。找到短信服务&#xff0c;设置短信签名&#xff08;上面图片的阿里云、菜鸟裹裹、天猫…&#xff09;&…

体验 Dolly V2

体验 Dolly V2 1. 什么是 Dolly V22. Github 地址3. 安装 Miniconda34. 创建虚拟环境5. 部署 Dolly V26. 编写测试程序7. 运行测试程序 1. 什么是 Dolly V2 Databricks的dolly-v2-12b&#xff0c;是一个在 Databricks 机器学习平台上训练的指令跟随型大型语言模型&#xff0c;…

【2023/05/06】EDSAC

Hello&#xff01;大家好&#xff0c;我是霜淮子&#xff0c;2023倒计时第1天。 Share 去成为你本该成为的人&#xff0c;任何时候都不会太晚。 &#xff08;出处&#xff1a;乔治艾略特&#xff09; Day1 EDSAC&#xff1a;存储程序式计算机的开山之作。 part1 EDSAC&…

redis(3)

redis客户端登陆: redis-cli -h host -p port -a password String类型的应用场景: 1)String通常用于保存单个字符串或者JSON格式的字符串数据 2)因为String类型通常是二进制安全的&#xff0c;因此你完全可以把一个图片内容当作字符串来进行存储 3)计数器&#xff0c;是常规的k…

java错题总结(31-33页)

假定Base b new Derived&#xff08;&#xff09;; 调用执行_360笔试题_牛客网 ABDC 只要是被子类重写的方法&#xff0c;不被super调用都是调用子类方法 ------------------------------------------------------------------------------------------- --------…

【Linux】Linux安装Redis(图文解说详细版)

文章目录 前言第一步&#xff0c;下载安装包第二步&#xff0c;上传安装包到/opt下&#xff08;老规矩了&#xff0c;安装包在opt下&#xff09;第三步&#xff0c;解压安装包第四步&#xff0c;编译第五步&#xff0c;安装第六步&#xff0c;配置redis第七步&#xff0c;设置开…

Matplotlib 初使用

五一假期时候&#xff0c;我在自己的电脑上安装了 matplotlib 安装步骤请转至&#xff1a;matplotlib 安装介绍 现在&#xff0c;我终于可以在自己电脑上学习 matolotlib 数学绘图库了&#xff0c; 说终于两个字&#xff0c;是完全是在安装过程中确实历经了折磨&#xff0c;根…

基于AT89C51单片机的电子钟设计与仿真

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/87759735?spm1001.2014.3001.5503 源码获取 主要内容&#xff1a; 设计一个电子钟,实现对时、分、秒的显示的控制,电路采用24小时计时方式。另一个功能是秒表功能…

mysql : name like “%name“; 索引一定失效吗?

场景如下&#xff1a; MySQL版本如下&#xff1a; 表结构如下&#xff1a;索引结构如下&#xff1a; 查询语句以及执行计划如下&#xff1a; 第一条查询语句&#xff1a; select name from em where name like ‘%b’; 可以发现使用了name 字段创建的索引 第二条查询语句&…

02-Java基础编程

Java基础编程 Java 基础语法Java 标识符变量变量的类型Java 基本数据类型基本数据类型转换 运算符常见运算符运算符的优先级 程序流程控制分支语句循环结构常用的循环结构循环的嵌套break 和 continue 关键字 数组一维数组多维数组的使用Arrays 工具类的使用数组中常见的异常 J…

AcWing——方格迷宫(有点不一样的迷宫问题)

4943. 方格迷宫 - AcWing题库 1、题目 给定一个 n 行 m 列的方格矩阵。 行从上到下依次编号为 1∼n&#xff0c;列从左到右依次编号为 1∼m。 第 i 行第 j 列的方格表示为 (i,j)。 矩阵中的方格要么是空地&#xff08;用 . 表示&#xff09;&#xff0c;要么是陷阱&#xf…

前后端身份认证:session身份认证,JWT认证

文章目录 前后端身份认证1、概述2、不同开发模式下的身份认证3、关于HTTP协议的无状态性4、Cookie4.1 介绍4.2 cookie特点4.3 cookie分类4.4 Cookie在session身份认证中的作用4.5 Cookie不具有安全性 5、Session认证5.1 Session认证过程5.2 Session认证原理图5.3 Session认证的…