初识C++ · 类和对象(中)(2)

前言:上篇文章已经介绍了6个默认成员函数中的3个函数,分别是构造函数,析构函数,拷贝构造函数,本文介绍的是后三个,赋值运算符重载const成员函数取地址操纵符重载


目录​​​​​​​

1 赋值运算符重载

1.1运算符重载

1.2 赋值重载

2 实现日期类


1 赋值运算符重载

1.1运算符重载

在学习赋值运算符重载之前,我们先来介绍一下运算符重载:
C++为了增加代码的可读性,引用了运算符重载的概念,运算符重载其实就是一个特殊一点点的函数,返回值,参数,返回类型都是有的,那么运算符重载后的函数名是什么呢?

运算符重载的关键字是operator,函数名就是operator加后面的运算符,比如:

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

operator==就是函数名,参数是两个日期类,返回值是true false,返回类型是bool。

其中,返回值,返回类型等都是根据实际情况操作的,比如日期减日期,返回类型是int,因为返回值是相差的天数,比如日期减天数,返回值就是日期类,因为返回值就是日期,所以实际的返回值返回类型参数等都是看实际情况的,没有固定的说法。

当我们比较一个数是否相等时候,我们直接:

int a = 0;
int b = 1;
bool ret = a == b;

直接利用运算符比较就行,但是对于日期类的我们不能这样干,因为直接比较的都是内置类型,在C++中可以直接进行比较的就是内置类型,但是自定义类型不可以直接比较,这个时候就需要用到运算符重载了,用法如:

bool operator==(Date d1, Date d2)
{
	return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}

这是函数的定法方法,那么定义的时候我们需要注意operator的使用:
1 不能通过连接其他符号来创建新的操作符:比如operator@

2 重载操作符必须有一个类类型参数

3 用于内置类型的运算符,其含义不能改变

4 作为类成员函数重载时,其形参看起来比操作数数目少1

5  .* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现

我们不能使用C++里面没有的符号来创建新的符号,因为没有实际意义,编译器也走不过去。

内置类型的比较我们可以直接调用运算符,编译器调用指令来执行代码,重载的操作符是用于自定义类型的,参数没有类是不行的,比如int operator-(int a,int b),两个内置类型重载完全没有意义,所以重载的操作符的参数一定要有个类。

其实严格意义上来说,重载操作符可以改变原来的含义,比如重载一个+,实现的时候实现的是减法,有问题吗?没有多大问题,可能是用来整蛊代码呢?但是实际写代码的时候还是不要改变的好,不然要挨打咯。

当重载函数作为成员函数的时候,因为成员函数都有个默认的成员函数指针,所以形参看起来比操作数少一个。

sizeof ?: . ::这几个操作符好说,不能重载,不常见的是.*,这个是成员函数指针,我们简单看一下函数指针的用法:

void Func()
{
	cout << "Func" << endl;
}
int main()
{
	void(*pf)() = Func;
	(*pf)();
	return 0;
}

其中函数指针类型是void()(),这里简单复习一下即可,因为函数名就是地址,所以函数指针取地址的时候是没有加&的,那么成员函数指针:

class Ob
{
public:
	void Func()
	{
		cout << "hahaha" << endl;
	}
};
int main()
{
	Ob o1;
	void(Ob:: *pf)() = &Ob::Func;
	(o1.*pf)();
	return 0;
}

成员函数指针和普通函数指针的差别就在于规定了成员函数指针取地址的时候必须加&,调用的方法如上,这个运算符也是不能被重载的。

operator介绍完了后,调用运算符重载函数分为显式调用和转换调用

int main()
{
	Date d1(2020, 1, 17);
	Date d2(2024, 2, 24);
	operator==(d1,d2);
	d1 == d2;
	return 0;
}

显式调用也就是把函数名全部写出来,当然比较麻烦,一般比较喜欢的就是转化调用了,直接d1==d2就可以了,称为转换调用的原因是因为编译器在汇编层面执行代码的时候也是调用的那个函数,即最后都是转换为了operator==(d1,d2)。

最后都是call重载函数。

那么是否现在意味着我们可以随意调用重载函数呢?

答案是不行的,因为全局重载函数要访问的话,类的成员变量一定要是公有的,不然就会报错:
C++提供了三个解决方法:

1 提供这些成员的get和set

2 使用友元

3 重载成成员函数

这里着重介绍的是重载成成员函数,友元后面介绍,get set其实java特别喜欢,大概使用如下:

int Getyear()
{
	return _year;
}
bool operator==(Date d1, Date d2)
{
	return d1.Getyear() == d2.Getyear();
}

了解一下,主要还是重载成成员函数。

	bool operator==(const Date& d)
	{
		return _year == d._year && _month == d._month && _day == d._day;
	}

重载之后,注意参数数目的改变。这里的显式调用就是d1.operator(d2)了。


1.2 赋值重载

Date operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
	return *this;
}
int main()
{
	Date d1(2020, 1, 17);
	Date d2(2024, 2, 24);
	Date d3 = d1;
	Date d4(1,1,1);
	d4 = d1;
	return 0;
}

我们先来看这样一段代码,d3 = d1,是拷贝还是赋值?d4 = d1是拷贝还是赋值?

答案比较明确,d3 = d1是拷贝构造,d4 = d1是赋值,那么为什么d4 = d1不是拷贝呢?拷贝是拷贝构造,也就是在初始化的时候完成的,d4已经初始化完成了,再调用就是赋值了。

那么再来一个问题:

int main()
{
	int i = 1, j = 2, m = 3;
	i = j = m;
	cout << i << endl;
	return 0;
}

请问赋值的顺序是什么样的?
这里的规定是先执行j =m,返回的是j,再执行i = j,最后赋值完成,也就是说连续赋值需要表达式有一个返回值,比如j = m的返回值就是j,那么执行重载后的连续赋值,就需要函数要有返回值:

那么j = m返回的是j,所以d2 = d3返回的应该是d2,函数的this指针指向的就是d2,也就是说我们要返回d2,但是d2在函数里面不是显式的,是隐式的,所以这个时候this指针就起作用了:
return *this就行。

Date operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
	return *this;
}

连续赋值的问题就结束了,那么引入一个新代码:

Date Func()
{
	Date d;
	return d;
}
int main()
{
	Func();
	return 0;
}

对构造函数和析构函数加一点打印,方便观察。

	Date(const Date& d)//拷贝构造函数
	{
		cout << "const Date& d" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	~Date()//析构函数
	{
		cout << "~Date" << endl;
		_year = _month = _day = 0;
	}

首先第一个问题,Func函数会调用几次拷贝构造函数和几次析构函数?

调用了一次拷贝构造函数和两次析构函数,可能会问了,明明没有拷贝拷贝复制的代码,怎么会有拷贝函数的调用呢?
这是因为临时变量的存在,创建好Date d之后,因为返回值是日期类,但是d在Func函数调用了之后会析构,因为生命周期已经结束了,那么返回的就是d拷贝给了一个临时变量,调用了拷贝构造函数,注意,这里是先调用的拷贝构造,再是发生了析构,最后就是主函数里面的d发生了析构。

那么,为了效率,可不可以减少拷贝构造函数的调用呢?析构函数是没有办法的,创建了生命周期一到就会销毁,但是我们可以想办法控制拷贝构造的调用。

Date& Func()
{
	Date d(2020,4,17);
	return d;
}
int main()
{
	const Date& d = Func();
	return 0;
}

如果返回值是日期类,并且用引用来接收的话,那就是一定要用const接收的,因为返回的日期类实际上是临时变量,临时变量具有常性,如果不用const接收就会导致权限的放大,编译器会报错的,使用引用是可以减少拷贝构造函数的调用的

这里观察的话Vs2022是看不出来的,2019可以看出来,因为2022的优化有点严重了,所以这里推荐用Vs2019学习。

使用引用返回就没有调用拷贝构造了,只有一次析构的原因是因为引用就是多个名指向一个空间,这里只创建了一个日期类,所以只用析构一次。

但是啊,这里引用返回又是一个很恐怖的事情,为什么呢?

这段代码的本意是:创建一个日期类d,再用一个日期类的引用来接受这个日期类的值。

但是经过了析构函数了之后,原本想的是值应该是2020.4.17,但是因为d析构了,所以主函数的d的值也会变成析构之后的值,但是这还不是最恐怖的,最恐怖的是野引用

Date& Func()
{
	Date d(2020,4,17);
	cout << &d << endl;
	return d;
}
int main()
{
	const Date& d = Func();
	cout << &d << endl;
	return 0;
}

首先两个d指向的空间确实是一样的,我们可以用这段代码观察。

但是随着Func函数的调用完成,也就是说Func函数创建的函数栈帧会被操作系统回收了,但是主函数的d仍然指向的是Func函数中d的那块空间,这就是一个野引用,因为栈帧是从上往下开辟的,如果我们再创建一个函数用来覆盖原来的函数:

Date& Func()
{
	Date d(2020,4,17);
	return d;
}

int F()
{
	int a = 1;
	int b = 2;
	int c = 3;
	return a + b + c;
}

int main()
{
	const Date& d = Func();
	F();
	return 0;
}

那么在Func函数调用完成了之后,Func的函数栈帧被回收,然后就是给F函数创建函数栈帧,因为栈帧是从上往下创建的,F函数的函数栈帧就会覆盖原来Func函数的栈帧,别忘记了此时主函数的d还是指向的那块空间,伴随着函数栈帧的创建,指向的那块空间会给上随机值,那么d就会变成:

d就会变成随机值,d的值可谓是大起大落,从期待的2020.4.17变成了1.1.1再变成了随机值,所以引用的使用也是有风险的,这里总结一下:

如果返回的是临时变量或者是局部变量,会被析构,那么就用传值返回,如果不会被析构函数析构的,那么就用传引用返回,可以减少拷贝构造函数使用次数,主要看的就是生命周期。

如下这种情况:

Date& Func()
{
	static Date d(2020,4,17);
	return d;
}
int main()
{
	const Date& d = Func();
	d.Print();
	return 0;
}
	void Print() const

如果创建的成员是静态的,出了函数不会被析构函数销毁,就用了传引用,这个打印的时候注意一下,后面介绍。

传值传引用大概就结束了,下面来看这段综合的代码:

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)
	{
		cout << "Date(const Date& d)" << endl;

		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	Date operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	~Date()
	{
		cout << "~Date()" << endl;
		_year = -1;
		_month = -1;
		_day = -1;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2024, 4, 14);
	Date d2(d1);
    Date d3 = d1;
	Date d4(2024, 5, 1);
	d1 = d4;
	d1 = d2 = d4;
	d1 = d1;
	return 0;
}

首先我们先采用传值调用看一遍,引入了一个自己传自己的例子,d1 = d1,严谨一点在重载=这里修改了一下,因为自己等于自己的话赋值没有必要,所以加个判断,直接返回就好了。

这里还有个小细节,前面说生命周期的问题,不免会有人谈论栈的问题,会说只要不在栈上就可以使用引用返回,但是这种想法是错误的,因为这种的默认思维是开辟的元素的栈就是函数的栈帧,但当我们看operator=函数的时候,返回是*this,请问*this在哪个的函数栈帧上?在主函数的函数栈帧上,这里也是栈,但是栈不同,我们可以返回值也可以返回引用,因为这个栈帧是主函数的,随程序的结束才会销毁,所以传值传引用看的是什么?是生命周期

传值调用:
Date d2(d1)一次拷贝构造,Date d3 = d1一次拷贝构造,d1 = d4一次拷贝构造,d2 = d4一次拷贝构造返回d1,d1 = d2一次拷贝构造返回d1,d1 = d1一次拷贝构造返回d1,共6次拷贝构造:

传引用调用:
Date d2(d1)一次拷贝构造,Date d3 = d1一次拷贝构造,后面调用重载函数的返回的都是引用,所以不会调用拷贝构造函数,共两次拷贝构造函数。

最后,赋值运算符重载是默认成员函数,即便用户自己不写,系统也会默认生成,默认生成的赋值运算符重载和拷贝构造函数的行为是一致的,以值的形式进行拷贝,对于日期类,MyQueue类来说默认生成的赋值重载就够用了,但是对于Stack类来说,需要进行深拷贝的类来说就需要用户自己显式定义赋值运算符重载:
读者可以自行注释掉operator=函数,系统也不会报错,因为调用了默认生成的赋值重载,行为就是拷贝构造函数,但是不是拷贝构造函数和赋值重载存在一个就行,存在即合理。

还有就是,赋值重载不能重载成全员函数,因为参数有个一定要是this指针,所以,只能重载成成员函数,这是赋值重载,结束!


2 实现日期类

了解完赋值重载后,我们现在就实现一个真正的日期类:

#pragma once
#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 1, 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);
	~Date();
private:
	int _year;
	int _month;
	int _day;
};

暂时先实现这几个简单的,因为是日期类的,所以拷贝构造函数和赋值重载不用显式定义,编译器生成的默认的就够用了,当然析构函数也是,这里看自己习惯咯,实现这些,其实实现一个就行了:

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}
void Date::Print()
{
	cout << _year << '-' << _month << '-' << _day << endl;
}
Date::~Date()
{
	_year = _month = _day = -1;
}

构造 打印 析构这三个函数就是信手拈来,都快写烂了的,这里有个需要注意的,缺省值是在声明的时候给的,在定义的时候又给缺省值就会重定义,编译器报错,在定义和声明的时候分离要注意这个点。

接着就是比较大小的函数:


bool Date::operator>(const Date& d)
{
	if (_year > d._year)
	{
		return true;
	}
	else if (_year == d._year)
	{
		if (_month > d._month)
		{
			return true;
		}
		else if (_month == d._month)
		{
			return _day > d._day;
		}
	}
	return false;
}

逻辑十分简单,就不过多介绍了。

接着就是其他函数:

bool Date::operator==(const Date& d)
{
	return  _year == d._year 
			&& _month == d._month 
			&& _day == d._day;
}
bool Date::operator<(const Date& d)
{
	return !(Date::operator>=(d));
}
bool Date::operator>=(const Date& d)
{
	return Date::operator>(d)
		|| Date::operator==(d);
}
bool Date::operator<=(const Date& d)
{
	return !(Date::operator>(d));
}
bool Date::operator!=(const Date& d)
{
	return !(Date::operator==(d));
}

写了一个函数,其他函数可以说是迎刃而解,比如我写了大于函数,大于等于函数就是大于的同时还满足等于就行,就很简单,无非就是反面正面,当然这段代码并不简洁,简洁一点就使用this指针:

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
		|| *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 !(*this ==d);
}
Date::~Date()
{
	_year = _month = _day = -1;
}

用this指针代替之后就会整洁很多了。

然后再来其他函数:

//+ -函数实现
Date& operator+=(int day);
Date& operator-=(int day);

	int GetMonthDay(int year,int month)
	{
		assert(month > 0 && month < 13);
		int yearArr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		if ((month == 2 )&&( year % 4 == 0 && year % 100 != 0 || year % 400 == 0))
		{
			return yearArr[2] + 1;
		}
		return yearArr[year];
	}

这里的话在头文件里面定义的日期类里面插入一个函数,它在+ -函数一定是会经常调用的,所以直接定义在类里面使它称为内联函数,直接定义在类里面的就是内联函数,声明和定义不分离,因为链接的时候符号表里面是没有这个函数的名字的。

日期加天数实现的逻辑也很简单,就是天数满了加月,月满了加年而已:

Date& Date::operator+=(int day)
{
	_day += day;
	while (_day < GetMonthDay(_year,_month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (13 == _month)
		{
			_year++;
			_month = 1;
		}
	}
	return *this;
}

逻辑很简单,-=同理。

Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += -day;
	}
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

这里-=要注意几个点,+=加的是本月的天数,-=加的是上月的天数,所以_day += GetMonthDay(_year,_month)的位置就应该放在判断完之后的位置,当然,有的时候整蛊一下别人,传个负数什么的,那就是反过来相加的事而已,返回去调用一下就可以了。

//+ -实现	
Date operator+(int day);
Date operator-(int day);

这里可以注意到的是这两个函数返回值是值,而不是引用,上面的函数就是引用,因为生命周期。

我们写代码的时候要灵活应用我们已经写过的函数,我们已经写了+=,复用一下就行了,它们唯一的区别就是改不改变已经创建好的日期类的变量。

Date Date::operator+(int day)
{
	Date tem = *this;
	tem += day;
	return tem;
}
Date Date::operator-(int day)
{
	Date tem = *this;
	tem -= day;
	return tem;
}

现在问题来了,是先实现+=,实现+的时候复用+=好呢还是反过来呢?

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 (13 == tmp._month)
		{
			tmp._year++;
			tmp._month = 1;
		}
	}
	return tmp;
}
Date& Date::operator+=(int day)
{
	*this = *this + day;
	return *this;
}

对比+=的代码,可以发现上面的代码的简洁度不是很高,而且开始和返回的时候都多了两个拷贝构造,效率降了一点,所以建议+复用+=的代码这种写法。

    //前置++ 后置++
	Date& operator++();//前置++
	Date operator++(int);//后置++
	//前置-- 后置--
	Date& operator++();//前置--
	Date operator++(int);//后置--

前置和后置的区别不用多说了,问题出在我们如何区分前置和后置,本贾尼博士在这里做出的改动是加了一个无用的int参数,有int参数的就是后置操作符,因为没有用,所以我们直接给一个int就行了,实现的话因为前置使用的是操作之前的,所以返回this指针,那么返回引用就会减少拷贝:

//前置++ 后置++
Date Date::operator++(int)//后置++
{
	Date tem = *this;
	*this += 1;
	return tem;
}
Date& Date::operator++()//前置++
{
	*this += 1;
	return *this;
}
//前置-- 后置--
Date& Date::operator--()//前置--
{
	*this -= 1;
	return *this;
}
Date Date::operator--(int)//后置--
{
	Date tem = *this;
	*this -= 1;
	return tem;
}

最后实现一个日期-日期,就得到了一个基本的日期类,但是怎么实现日期-日期呢?因为每个月的天数都不同,所以相对来说有点麻烦,这里用一个比较巧妙的思路:
刚才写了后置前置++,复用呗,比如一个数加到一个数,只要不等于,一直++就可以了,谁大谁小就交给假设法:

	int operator-(const Date& d);//日期相减
int Date::operator-(const Date& d)//日期相减 
{
	Date max = *this;
	Date min = d;
	int flag = 1, num = 0;
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	while (max != min)
	{
		min++;
		num++;
	}
	return num * flag;
}

一个完整的日期类就写好了。


感谢阅读!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

全世界IT人苦竞业久矣!美国FTC宣布全面废除员工竞业协议

2023 年 1 月&#xff0c;美国联邦贸易委员会&#xff08;FTC&#xff09;发布声明称&#xff0c;拟在全国范围禁止用人单位与雇员签订竞业禁止性条款。当地时间 4 月 23 日&#xff0c;FTC 宣布全面禁止所有员工&#xff08;包括高级管理人员&#xff09;签署新的竞业禁止协议…

Vue3+Echarts: 浏览器缩小后,图表内容发生重叠

一、问题 Vue3Echarts项目&#xff1a;浏览器缩小后&#xff0c;图表内容发生重叠。本文将提供几个解决上述问题的思路&#xff0c;后续有新的解决思路将在此处进行补充。 二、解决思路 1、动态调整ECharts配置 如果图表容器的尺寸没有随着浏览器窗口的缩小而进行相应地调整…

[Linux_IMX6ULL驱动开发]-设备树简述

目录 设备树的引入 设备树具体框架 设备树的属性 label address-cells和size-cells compatible model status reg 设备树的编译 内核对设备树的处理 plateform_device如何对应plateform_driver 设备树的引入 之前已经学习了解过了总线驱动模型的概念&#xff0c;也…

分类预测 | Matlab实现CNN-BiLSTM-SAM-Attention卷积双向长短期记忆神经网络融合空间注意力机制的数据分类预测

分类预测 | Matlab实现CNN-BiLSTM-SAM-Attention卷积双向长短期记忆神经网络融合空间注意力机制的数据分类预测 目录 分类预测 | Matlab实现CNN-BiLSTM-SAM-Attention卷积双向长短期记忆神经网络融合空间注意力机制的数据分类预测分类效果基本描述程序设计参考资料 分类效果 基…

excel相同行不同列查询

EXCEL中e列和f列是每一行对应的&#xff0c;我想在d列中找和e列一样的元素&#xff0c;然后获取同一行中f列的值 IFERROR(VLOOKUP(D1, E:F, 2, FALSE), "")

STC8H8K64U I2C主机模式相关寄存器

STC8H8K64U I2C主机模式相关寄存器 STC8H8K64U-TSSOP20 I2CCFG I2C配置寄存器 I2CMSCR I2C主机控制寄存器 I2CMSST I2C主机状态寄存器 I2CMSAUX I2C主机辅助控制寄存器 I2CTXD I2C数据发送寄存器 I2CRXD I2C数据接收寄存器 I2CCFG I2C配置寄存器 B7ENI2C ENI2C&#xff1a…

【电子元件】常用的二极管、极管规格参数一览表

目录 1. 常用的二极管规格参数1.1 贴片二极管1.2 直插二极管 2. 常用的三极管规格参数2.1 贴片三极管2.2 直插三极管 参考资料 1. 常用的二极管规格参数 1.1 贴片二极管 型号/封装丝印正向压降(Vf) 反向击穿电压(Vr)平均整流电流(Io)/正向工作电流(If)反向电流(Ir)反向恢复时间…

实验:使用apache + yum实现自制yum仓库

实验准备 Web服务器端&#xff1a;cenos-1&#xff08;IP&#xff1a;10.9.25.33&#xff09; 客户端&#xff1a;centos-2 保证两台机器网络畅通&#xff0c;原yum仓库可用&#xff0c;关闭防火墙和selinux Web服务器端 ①安装httpd并运行&#xff0c;设置开机自启动 安装…

2024五一萌趣嘉年华主题展活动策划案

2024五一国宝大作战 萌趣嘉年华熊猫滚滚来野主题展活动策划案-53P 活动策划信息&#xff1a; 方案页码&#xff1a;53页 文件格式&#xff1a;PPT 方案简介&#xff1a; 活动思路&#xff1a; 五一马上就要到了~再加上全民关注的对象--大熊猫&#xff01;&#xff01; 这…

Echarts异步数据与动画加载

目录 简介 头部代码 这段代码是使用 Echarts 绘制图表的关键部分。首先&#xff0c;初始化了一个 Echarts 实例。然后&#xff0c;通过 Ajax 请求获取数据&#xff0c;并基于此设置图表选项。其中包括颜色、背景色、标题、提示框、图例以及饼图的具体配置。 具体解释如下&a…

面试二十一、红黑树

性质&#xff1a; 插入&#xff1a; 旋转&#xff1a;

【论文阅读】互连网络的负载平衡路由算法 (RLB RLBth)

前言Oblivious Load Balancing 不经意路由负载平衡 1. oblivious routing 不经意/无关路由的背景知识 1. oblivious routing, adaptive routing & minimal/non-minimal routing algorithms 2. Balancing a 1-Dimensional ring: RLB and RLBth 一维 ring 的 RLB and RLBth 1…

强力的应用容器引擎---------Docker的资源控制

目录 一、CPU 资源控制 1.1cgroups有四大功能 1.2设置CPU使用率上限 1.2.1查看CPU使用率 1.2.2进行CPU压力测试 1.2.3设置50%的比例分配CPU使用时间上限 1.3设置CPU资源占用比&#xff08;设置多个容器时才有效&#xff09; 1.3.1创建两个容器为hua1 和hua2&#xff0c…

The_Maya_Society

突然发现自己做了一些逆向题都没有写笔记 今天&#xff0c;发现这道题有意思 1.解压文件 三个文件The Maya Society.html&#xff0c;maim.cc,maya.png 当时我看到这个题的时候&#xff0c;我以为是不是会是js逆向 看来是我蠢了 这三个文件&#xff0c;main.css和maya.png这两…

【算法分析与设计】重复的DNA

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 题目 DNA序列 由一系列核苷酸组成&#xff0c;缩写为 A, C, G 和 T.。 例如&#xff0c;"ACGAATTCCG" 是一个 DNA序列 。 在研究…

libVLC 制作一款精美的播放器

1.简介 本文将简单介绍使用libVLC制作一款精美的播放器。 开发环境:Visual Studio + Qt插件。 Qt版本:Qt5.9。 libVLC版本:3.0.20。 以下是运行界面效果图:截取其中几张。 右键菜单,功能还是比较齐全。 2.ui界面构成 接下来简单介绍一下ui界面构成。 主界面由播放树…

二维码图片的链接怎么提取?在线获取解码链接的方法

随着现在二维码成为内容展示的主要用途&#xff0c;很多场景下都会需要通过扫码的方式在手机上获取内容。那么在遇到无法扫码的情况时&#xff0c;可以通过提取二维码短链接来访问内容&#xff0c;点击链接跳转到对应的内容页面。 二维码链接想要快速的提取出来&#xff0c;最…

在 vue3 中使用高德地图

前言&#xff1a;定位地图位置所需要的经纬度&#xff0c;可以通过 拾取坐标 获取。 一&#xff1a;快速上手 1. 安装依赖 npm install amap/amap-jsapi-loader # or pnpm add amap/amap-jsapi-loader # or yarn add amap/amap-jsapi-loader 2. 创建组件 src/components/Ma…

面向对象三大特征(python)

目录 1. 封装 为什么使用封装&#xff1f; 如何实现封装&#xff1f; 一个简单的封装示例 二.继承 为什么使用继承&#xff1f; 如何实现继承&#xff1f; 一个简单的继承示例 使用继承的好处 三.多态 为什么使用多态&#xff1f; 如何实现多态&#xff1f; 一个简…

面向对象介绍

1、面向对象程序设计 2、数据抽象 3、实体、对象、类之间的关系 4、从计算机的观点看对象 5、抽象 6、封装 7、继承 8、多态 9、面向对象编程方法的特性 10、面向对象编程的优缺点