【C++】类和对象(中)之拷贝构造与运算符、操作符重载

👀樊梓慕:个人主页

 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》

🌝每一个不曾起舞的日子,都是对生命的辜负


前言

我们继续学习默认成员函数,本篇文章博主带来的是拷贝构造函数与运算符、操作符重载的讲解,并且还有const成员所带来的一系列问题,最后博主会给大家贴出利用前面所学知识写出的日期类。


欢迎大家📂收藏📂以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。

=========================================================================

GITEE相关代码:🌟fanfei_c的仓库🌟

=========================================================================


1.拷贝构造函数

1.1概念

在介绍拷贝构造函数之前,我想先举一个例子来告诉大家为什么会有拷贝构造?

比如:

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};int main()
{Date d1;Date d2(d1);//这里是什么行为?return 0;
}

请观察我在代码中标识的部分,很明显这里是将d1作为实参传递给构造函数。

我们之前学习函数时,曾说过,形参是实参的一份临时拷贝

那么好了,这就是拷贝构造函数存在的其中一个意义。


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


1.2特征

拷贝构造函数也是特殊的成员函数,其特征如下:

1.拷贝构造函数是构造函数的一个重载形式。

2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发❓无穷递归调用❓。


🌀为什么这里会引发无穷递归调用🌀


前面对于拷贝构造函数的定义是这样说的:拷贝构造函数在用已存在的类类型对象创建新对象时由编译器自动调用。

我们观察下面的代码:

Date(const Date date)//你会发现这里报错了??
{_year = date._year;_month = date._month;_day = date._day;
int main()
{Date d1;Date d2(d1);
}

 当d1作为参数传递给拷贝构造函数时,是不是会再次调用拷贝构造函数??

那我们就找到为什么会引发无穷递归调用的答案了。


解决的方案就是特征所描述的那样:拷贝构造函数的参数只有一个且必须是类类型对象的引用。 

也就是这样:

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

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


4.编译器生成的默认拷贝构造函数(浅拷贝)已经可以完成字节序的值拷贝了,还需要自己显式实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?

typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申请空间失败");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:DataType* _array;size_t _size;size_t _capacity;
};int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2(s1);return 0;
}

程序报错:


🔒那么对于日期类,他所做的拷贝就是浅拷贝,那浅拷贝会不会有风险存在呢🔒


对于日期类,浅拷贝没有风险。

但是对于自己开了空间的自定义类型来讲,浅拷贝可能会引发大麻烦

 大家还记得之前学习的析构函数么,通过前面的学习我们知道,析构函数会在对象销毁时时自动调用。

那么如果对于自己开了空间的自定义类型来讲,单纯的进行浅拷贝,就会有两个对象的两个指针指向相同的空间,那这两个对象在销毁时,自动调用析构函数,就会发生同一块空间释放两次的情况,显然程序会崩溃。


 那如何避免这样的问题呢,很显然我们需要像构造函数那样,自己设计一个函数用来进行拷贝,这样的拷贝被称为深拷贝

一般比如顺序表、链表、二叉树等开了空间的自定义类型都需要深拷贝。


那么我们可以得到结论:

  1. 内置类型成员完成值拷贝;
  2. 自定义类型调用该成员的拷贝构造。

那么都有什么样的场景用到了拷贝构造呢?

当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用。

以下情况都会调用拷贝构造函数:

① 程序中需要新建立一个对象,并用另一个同类的对象对它初始化,如前面介绍的那样。

② 当函数的参数为类的对象时。在调用函数时需要将实参对象完整地传递给形参,也就是需要建立一个实参的拷贝,这就是按实参复制一个形参,系统是通过调用复制构造函数来实现的,这样能保证形参具有和实参完全相同的值。

③ 函数的返回值是类的对象。在函数调用完毕将返回值带回函数调用处时。此时需要将函数中的对象复制一个临时对象并传给该函数的调用处。 


1.3补充的一些知识

首先再来给大家强化一下构造和拷贝构造的理解。

观察下面的代码:

int main()
{A aa1(1);    //构造// 一个已经存在的对象拷贝初始化另一个要创建的对象,就是拷贝构造A aa2(aa1);  // 拷贝构造A aa3 = aa1; // 拷贝构造 or 赋值拷贝 -> 拷贝构造// 两个已经存在的对象拷贝是赋值拷贝aa2 = aa3;return 0;
}

 本质上讲,A aa2(aa1)和A aa2=aa1两种写法是一样的,都属于拷贝构造。

 博主还会在接下来的文章中,介绍更多有关构造和拷贝构造的内容,这里的内容比较琐碎,目前的知识支撑还不够,所以我们还要往后学几章。


2.运算符重载

2.1运算符重载

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

函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@。
  • 重载操作符必须有一个类类型参数。
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义。
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this。
  • .*  ::  sizeof  ?:(三目操作符)  .  注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

我们来实现一个运算符==的重载试试看:

大家同时思考一个问题:运算符重载函数是放在类内部,还是全局呢?

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//private:    这里是为了示例,一般成员变量为私有int _year;int _month;int _day;
};
// 这里会发现运算符重载成全局的就需要成员变量是公有的,那么问题来了,封装性如何保证?
bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}
void Test()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);cout << (d1 == d2) << endl;
}

我们发现如果运算符重载成全局的,一般来讲类的成员变量都是私有的,那么如何访问成员变量进行运算呢?

这里其实可以用我们后面学习的友元解决,或者干脆重载成成员函数。

由于友元我们还没讲,那就先试试看将运算符重载函数放到类内部。

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_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;
};

还记得我们前面学习的this指针是隐藏的参数吧。

我们知道==运算符需要两个操作数,所以运算符重载函数只需要一个显式参数,另一个为隐藏参数*this。 


2.2赋值运算符重载

1. 赋值运算符重载格式

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

class Date
{
public:Date(int year = 1900, 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;}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;
};

2.赋值运算符只能重载成类的成员函数不能重载成全局函数(和普通运算符的区别)

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}int _year;int _month;int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}
// 编译失败:
// error C2801: “operator =”必须是非静态成员

原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。


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

注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

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 = 1970;int _month = 1;int _day = 1;// 自定义类型Time _t;
};
int main()
{Date d1;Date d2;d1 = d2;//重载赋值运算符return 0;
}

有没有发现这里和构造函数仍然十分相似。

内置类型执行浅拷贝,自定义类型调用自己的重载赋值运算符。 

为什么自定义类型不能简单的进行浅拷贝,而还要自己设计深拷贝?

相信前面讲解的拷贝函数的二次析构问题已经给了你答案, 我们再在这里举一个例子加深大家的印象。

// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申请空间失败");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:DataType* _array;size_t _size;size_t _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2;s2 = s1;//这里是默认的赋值运算符,完成的是浅拷贝return 0;
}

如果前面的二次析构你理解了的话,这里不是问题。

分析:

总结一下:

  • 如果类中未涉及到资源管理,赋值运算符是否实现都可以;
  • 一旦涉及到资源管理则必须要实现。

2.3流运算符重载

流运算符设计的初衷是为了解决自定义类型的输入和输出问题。

那么就一定会涉及到流运算符重载的相关内容。

但是流运算符又不能和其他运算符一样重载到类内部,相反,流运算符必须实现到全局,这是为什么呢?

我们知道成员函数有一个隐藏的参数this,流运算符也是一个双目操作符,一般流运算符我们写为cout<<a;

但假设我们将流运算符重载到类内部作成员函数的话,第一个参数为this,所以好像我们这样写才是对的a<<cout;

也就是说我们只能也必须将流运算符重载实现到全局,才能避免this作第一个参数。

我们试试看:

对啊,定义到全局,就访问不了私有的成员变量了呀。

那这里一般我们会采用友元函数解决成员变量私有的问题。(友元函数会在后续介绍)

比如:

class Date
{
public:Date(int year = 1, int month = 1, int day = 1);// 友元函数friend void operator<<(ostream& out, const Date& d);
private:int _year;int _month;int _day;
};void operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
}

这样就可以解决类外部的函数无法访问到私有成员变量的问题了。 

可是我们在使用时发现,<<是需要返回值的,比如这种cout<<a<<b<<endl;

那我们修改一下函数实现:

class Date
{
public:Date(int year = 1, int month = 1, int day = 1);// 友元函数friend ostream& operator<<(ostream& out, const Date& d);
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}

 完整的输入输出流重载函数:

class Date
{
public:Date(int year, int month, int day)	// 构造函数{_year = year;_month = month;_day = day;}friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& out, const Date& d)
{cout << d._year << "/" << d._month << "/" << d._day;return out;
}istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}int main()
{int a = 0;int b = 1;cout << a << " " << b << endl;Date d1(2022, 11, 27);Date d2(2022, 11, 28);cout << d1 << " " << d2 << endl;cin >> d2;cout << d2;return 0;
}

2.4前置++与后置++重载

前置++与后置++的区别是什么呢?

一个是先加后用,一个则是先用后加。

还有另一个问题,他们的符号都是++,那怎么区分呢?
C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递。

我们直接看实现:

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// 前置++:返回+1之后的结果// 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率Date& operator++(){_day += 1;return *this;}// 后置++:// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this + 1// 而temp是临时对象,因此只能以值的方式返回,不能返回引用Date operator++(int){Date temp(*this);_day += 1;return temp;}
private:int _year;int _month;int _day;
};
int main()
{Date d;Date d1(2022, 1, 13);d = d1++; // d: 2022,1,13 d1:2022,1,14d = ++d1; // d: 2022,1,15 d1:2022,1,15return 0;
}

3.const成员函数

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改

比如这样:


 观察下面的代码:

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << "Print()" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}void Print() const{cout << "Print()const" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}
private:int _year; // 年int _month; // 月int _day; // 日
};
void Test()
{Date d1(2022, 1, 13);d1.Print();const Date d2(2022, 1, 13);d2.Print();
}

请思考下面的几个问题:
1. const对象可以调用非const成员函数吗?
2. 非const对象可以调用const成员函数吗?
3. const成员函数内可以调用其它的非const成员函数吗?
4. 非const成员函数内可以调用其它的const成员函数吗? 


 之前在学习引用时,我们曾经讨论过权限缩小和放大的问题👉樊梓慕-引用

权限只能缩小,不能放大。

🔓解释一下🔓

也就是说const的对象在引用时,引用须加const,如果不加,此时引用的权限大于被引用的const对象,这就是所谓的权限不能放大。

但是不加const的对象在引用时,引用可加可不加const,因为权限是平级或是缩小了的,这就是所谓的权限只能缩小。


那搞清楚了这个概念,再看前面的题目有没有思路了呢?

答案:

  1. ❎权限被放大;
  2. ✅权限被缩小;
  3. ❎权限被放大;
  4. ✅权限被缩小;

📣📣📣成员函数定义的原则📣📣📣

  1. 能定义成const的成员函数都应该定义成const(这里的const是指参数列表后面的const,也就是修饰this的const),这样const对象和非const对象(这里的const是指修饰对象的类型为const)都可以调用;比如下面图片,属于const对象调用非const成员函数,我们刚做的小题已经告诉我们这样是权限放大的问题了,所以现在只需要将成员函数设计为const成员函数,将this的类型改为const Date* const this,就可以解决这一问题。

  2. 要修改成员变量的成员函数,不能定义成const。

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

这两个默认成员函数一般不用重新定义,编译器默认生成。

class Date
{
public:Date* operator&(){return this;}const Date* operator&()const{return this;}
private:int _year; // 年int _month; // 月int _day; // 日
};

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


5.日期类的实现

class Date
{// 获取某年某月的天数int GetMonthDay(int year, int month){static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };int day = days[month];if (month == 2&& ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))){day += 1;}return day;}// 全缺省的构造函数Date(int year = 1900, int month = 1, int day = 1){if (year < 1900|| month < 1 || month > 12|| day < 1 || day > GetMonthDay(year, month)){cout << "非法日期" << endl;}_year = year;_month = month;_day = day;}// 拷贝构造函数// d2(d1)Date(const Date& d){this->_year = d._year;_month = d._month;_day = d._day;}// 赋值运算符重载// d2 = d3 -> d2.operator=(&d2, d3)Date& operator=(const Date& d){if (this != &d){this->_year = d._year;this->_month = d._month;this->_day = d._day;}return *this;}// 析构函数~Date(){// 清理工作}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}// 日期+=天数// d1 += 10// d1 += -10Date& operator+=(int day){if (day < 0){return *this -= -day;}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_year++;_month = 1;}}return *this;}// 日期+天数// d + 10Date operator+(int day){Date ret(*this);ret += day;return ret;}// 日期-天数Date operator-(int day){Date ret(*this);ret -= day;return ret;}// 日期-=天数// d -= 100// d -= -100Date& 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;}// 前置++// ++d -> d.operator++(&d)Date& operator++(){*this += 1;return *this;}// 后置++// d++ -> d.operator++(&d, 0)Date operator++(int){Date ret(*this);*this += 1;return ret;}// // 后置--Date operator--(int){Date ret(*this);*this -= 1;return ret;}// 前置--Date& operator--(){*this -= 1;return *this;}// d1 > d2// >运算符重载bool 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){if (_day > d._day){return true;}}}return false;}// ==运算符重载bool operator==(const Date& d){return _year == d._year&& _month == d._month&& _day == d._day;}// 下面复用上面两个的实现// >=运算符重载bool operator >= (const Date& d){return *this > d || *this == d;}// <运算符重载bool operator < (const Date& d){return !(*this >= d);}// <=运算符重载bool operator <= (const Date& d){return !(*this > d);}// !=运算符重载bool operator != (const Date& d){return !(*this == d);}// d1 - d2// 日期-日期 返回天数int operator-(const Date& d){int flag = 1;Date max = *this;Date min = d;if (*this < d){max = d;min = *this;flag = -1;}int day = 0;while (min < max){++(min);++day;}return day * flag;}
private:int _year;int _month;int _day;
};

这里需要注意的点是:已经实现了的运算符重载可以复用,逻辑转化就好了,没必要每一个都要完全实现。


最近的内容很碎,大家只需要跟着博主一起慢慢学习就好,内容完全展开后就豁然开朗啦


=========================================================================

如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容

🍎博主很需要大家的支持,你的支持是我创作的不竭动力🍎

🌟~ 点赞收藏+关注 ~🌟

========================================================================= 

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

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

相关文章

③ 软件工程CMM、CMMI模型【软考中级-软件设计师 考点】

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ ③ 软件工程CMM、CMMI模型【软考中级-软件设计…

Android 数据恢复的顶级软件分享

人们经常向我们询问有关 Android 数据恢复软件的信息以及它们是否有用。而且&#xff0c;我们给他们讲了两个朋友的故事——凯伦和亚历克斯。他们都丢失了 Android 手机上的一些重要数据。 丢失数据确实是一个令人心碎的时刻&#xff0c;根据丢失的文件&#xff0c;可能会让您…

橙河网络:坏人是怎么形成的?

小A是一个非常热心的人&#xff0c;给谁帮忙&#xff0c;都免费。 大家都说&#xff0c;小 A&#xff0c;实在人呀&#xff0c;哈哈。 小B搬来了&#xff0c;他活多&#xff0c;弄不过来&#xff0c;常找小 A 来帮忙。 小A 每次来帮忙&#xff0c;小B 都给小A一张大团结(100…

oracle (8)Managing Tablespace Data File

目录 一、基础知识 1、表空间和数据文件 2、存储层次结构摘要 3、表空间的类型 4、表空间中的空间管理 5、临时表空间 6、Default Temporary TS 默认临时TS 二、常用实操 1、Creating Tablespaces创建表空间 2、Dictionary-Managed TS 字典管理的表空间 3、Locally …

【idea】生成banner.txt

Spring Boot banner在线生成工具&#xff0c;制作下载英文banner.txt&#xff0c;修改替换banner.txt文字实现自定义&#xff0c;个性化启动banner-bootschool.netSpring Boot banner工具实现在线生成banner&#xff0c;轻松修改替换实现自定义banner&#xff0c;让banner.txt文…

pix2tex - LaTeX OCR 安装使用记录

系列文章目录 文章目录 系列文章目录前言一、安装二、使用三、如果觉得内容不错&#xff0c;请点赞、收藏、关注 前言 项目地址&#xff1a;这儿 一、安装 版本要求 Python: 3.7 PyTorch: >1.7.1 安装&#xff1a;pip install "pix2tex[gui]" 注意&#xff1a…

会声会影2024对比2023变化以及功能对比

全新会声会影2024版本现已登场&#xff0c;小伙伴们相信已经急不可待地想知道2024版到底有哪些新功能。对比2023版本&#xff0c;会声会影2024版本有没有功能的增强&#xff1f;事不宜迟&#xff0c;现在就让我们一起来看看会声会影2024对比2023的变化&#xff0c;包括功能对比…

华为是怎么做全面预算管理的?

大家好&#xff0c;我是老原。 在之前的文章分享中&#xff0c;都穿插着一个很重要但是不被你们重视的的部分&#xff0c;就是预算管理这块。 有很多新手项目经理在做计划的时候&#xff0c;发现自己设备和步骤都不熟悉&#xff0c;根本无从下手&#xff0c;不知道怎么做。 …

XXL-JOB默认accessToken身份绕过RCE漏洞复现 [附POC]

文章目录 XXL-JOB默认accessToken身份绕过RCE漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 XXL-JOB默认accessToken身份绕过RCE漏洞复现 [附POC] 0x01 前言 免责声明&#xff1a;请勿利用文章内的相关技术从…

Java--网络通信

1.端口Port&#xff1a; 通常计算机上提供了HTTP,FTP等多种服务&#xff0c;客户机通过不同的端口来确定连接到服务器的哪项服务上。 2.套接字Socket&#xff1a; 套接字Socket用于将应用程序与端口连接起来。套接字是一个假想的链接装置。 3.InetAddress类 java.net包中的Ine…

VSCode 如何设置背景图片

VSCode 设置背景图片 1.打开应用商店&#xff0c;搜索 background &#xff0c;选择第一个&#xff0c;点击安装。 2. 安装完成后点击设置&#xff0c;点击扩展设置。 3.点击在 settings.json 中编辑。 4.将原代码注释后&#xff0c;加入以下代码。 // { // "workben…

SpringCloudTencent(上)

SpringCloudTencent 1.PolarisMesh介绍2.北极星具备的功能3.北极星包含的组件4.功能特性1.服务管理1.服务注册2.服务发现3.健康检查 2.配置管理 5.代码实战1.环境准备2.服务注册与发现3.远程调用 1.PolarisMesh介绍 1.北极星是腾讯开源的服务治理平台&#xff0c;致力于解决分…

RESTful接口实现与测试

目录标题 是什么&#xff1f;设计风格HTTP协议四种传参方式常用注解RequestBody与ResponseBodyRequestMapping注解RestController与ControllerPathVariable 与RequestParam 接受复杂嵌套对象参数Http数据转换的原理自定义HttpMessageConverter统一规划接口响应的数据格式实战&a…

【使用Python编写游戏辅助工具】第一篇:概述

引言 欢迎阅读本系列文章&#xff0c;本系列将带领读者朋友们使用Python来实现一个简单而有趣的游戏辅助工具。 写这个系列的缘由源自笔者玩了一款游戏。正巧&#xff0c;笔者对Python编程算是有一定的熟悉&#xff0c;且Python语言具备实现各种有趣功能的能力&#xff0c;因…

MySQL复习总结(一):基础篇

文章目录 一、MySQL概述二、SQL语句2.1 SQL分类2.2 DDL语言2.2.1 数据库操作2.2.2 表操作:通用2.2.3 表操作:修改2.2.4 表操作:删除 2.3 DML语言2.3.1 添加数据2.3.2 修改数据2.3.3 删除数据 2.4 DQL语言2.5 DCL语言 三、函数四、约束五、多表查询5.1 多表关系 六、事务6.1 事务…

Compose-Multiplatform在Android和iOS上的实践

本文字数&#xff1a;4680字 预计阅读时间&#xff1a;30分钟 01 简介 之前我们探讨过KMM&#xff0c;即Kotlin Multiplatform Mobile&#xff0c;是Kotlin发布的移动端跨平台框架。当时的结论是KMM提倡将共有的逻辑部分抽出&#xff0c;由KMM封装成Android(Kotlin/JVM)的aar和…

【LeetCode:80. 删除有序数组中的重复项 II | 双指针】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

DbUtils + Druid 实现 JDBC 操作 --- 附BaseDao

文章目录 Apache-DBUtils实现CRUD操作1 Apache-DBUtils简介2 主要API的使用2.1 DbUtils2.2 QueryRunner类2.3 ResultSetHandler接口及实现类 3 JDBCUtil 工具类编写3.1 导包3.2 编写配置文件3.3 编写代码 4 BaseDao 编写 Apache-DBUtils实现CRUD操作 1 Apache-DBUtils简介 com…

pytorch+LSTM实现使用单参数预测,以及多参数预测(代码注释版)

开发前准备&#xff1a; 环境管理&#xff1a;Anaconda python: 3.8 显卡&#xff1a;NVIDIA3060 pytorch: 到官网选择conda版本&#xff0c;使用的是CUDA11.8 编译器&#xff1a; PyCharm 简述&#xff1a; 本次使用seaborn库中的flights数据集来做试验&#xff0c;我们通过…

代码训练营第59天:动态规划part17|leetcode647回文子串|leetcode516最长回文子序列

leetcode647&#xff1a;回文子串 文章讲解&#xff1a;leetcode647 leetcode516&#xff1a;最长回文子序列 文章讲解&#xff1a;leetcode516 DP总结&#xff1a;动态规划总结 目录 1&#xff0c;leeetcode647 回文子串。 2&#xff0c;leetcode516 最长回文子串&#xff1…